[
  {
    "path": ".clang-format",
    "content": "BasedOnStyle: \"LLVM\"\n\nBreakBeforeBraces: Linux\nStandard: Cpp11\nColumnLimit: 80\n\nMaxEmptyLinesToKeep: 2\n\nNamespaceIndentation: All\n\nAlignAfterOpenBracket: true\nAlwaysBreakTemplateDeclarations: true\nBinPackArguments: false\nBinPackParameters: false\nPointerAlignment: Right\nAllowShortFunctionsOnASingleLine: Empty\n\nBreakConstructorInitializersBeforeComma: true\n"
  },
  {
    "path": ".clang-tidy",
    "content": "Checks: >\n  -*,\n  cppcoreguidelines-pro-type-static-cast-downcast,\n  google-readability-casting,\n  modernize-*,\n  -modernize-concat-nested-namespaces,\n  -modernize-pass-by-value,\n  -modernize-use-nodiscard,\n  -modernize-use-trailing-return-type,\n  mpi-*,\n  performance-*,\n  -performance-inefficient-string-concatenation,\n  -performance-no-automatic-move,\n  -performance-avoid-endl\n"
  },
  {
    "path": ".clangd",
    "content": "# Work around our crazy compilation setup:\nCompileFlags:\n  Add: [\n    -I../source/euler,\n    -DRYUJIN_VERSION=\"\",\n    -DRYUJIN_GIT_REVISION=\"\"\n    -DRYUJIN_GIT_SHORTREV=\"\"\n  ]\n"
  },
  {
    "path": ".gitattributes",
    "content": ".gitattributes export-ignore\n.gitignore export-ignore\n.gitmodules export-ignore\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "# Set update schedule for GitHub Actions\n\nversion: 2\nupdates:\n\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      # Check for updates to GitHub Actions every week\n      interval: \"weekly\"\n"
  },
  {
    "path": ".github/workflows/indent.yml",
    "content": "name: indentation\n\non: [push, pull_request]\n\nconcurrency:\n  group: ${ {github.event_name }}-${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: ${{github.event_name == 'pull_request'}}\n\npermissions:\n  contents: read\n\njobs:\n\n  ################\n  # clang-format #\n  ################\n\n  clang-format:\n    name: clang-format\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v3\n      with:\n        submodules: 'true'\n    - name: clang-format\n      uses: DoozyX/clang-format-lint-action@v0.16.2\n      with:\n        source: 'source tests'\n        clangFormatVersion: 16.0.3\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "name: verification\n\non:\n  push:\n  pull_request:\n    types:\n    - opened\n    - reopened\n    - synchronize\n    - ready_for_review\n\nconcurrency:\n  group: ${ {github.event_name }}-${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: ${{github.event_name == 'pull_request'}}\n\npermissions:\n  contents: read\n\njobs:\n\n  #############################################\n  # ubuntu-lts-24.04 with gcc, deal.II master #\n  #############################################\n\n  ubuntu-lts-2404-dealii-master-release:\n    name: tests-release-deal.II-master\n    runs-on: [ubuntu-24.04]\n\n    container:\n      options: --user root\n      image: dealii/dealii:master-noble\n\n    steps:\n    - uses: actions/checkout@v3\n      with:\n        submodules: 'true'\n    - name: info\n      run: |\n        g++ -v\n        cmake --version\n    - name: configure release\n      run: |\n        mkdir build-release\n        cd build-release\n        cmake -DWITH_OPENMP=on ..\n    - name: build release\n      run: |\n        cd build-release\n        make VERBOSE=1 -j2\n    - name: test release\n      run: |\n        export OMPI_ALLOW_RUN_AS_ROOT=1\n        export OMPI_ALLOW_RUN_AS_ROOT_CONFIRM=1\n        cd build-release\n        # skip a number of expensive verification tests:\n        ctest --output-on-failure -j2 -VV -E \\\n          \"(verification-isentropic_vortex.*l7|verification-becker_solution.*l7)\"\n\n  #############################################\n  # ubuntu-lts-24.04 with gcc, deal.II master #\n  #############################################\n\n  ubuntu-lts-2404-dealii-master-debug:\n    name: tests-debug-deal.II-master\n    runs-on: [ubuntu-24.04]\n\n    container:\n      options: --user root\n      image: dealii/dealii:master-noble\n\n    steps:\n    - uses: actions/checkout@v3\n      with:\n        submodules: 'true'\n    - name: info\n      run: |\n        g++ -v\n        cmake --version\n    - name: configure debug\n      run: |\n        mkdir build-debug\n        cd build-debug\n        cmake -DWITH_OPENMP=on -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS_DEBUG=\"-O2\" ..\n    - name: build debug\n      run: |\n        cd build-debug\n        make VERBOSE=1 -j2\n    - name: test debug\n      run: |\n        export OMPI_ALLOW_RUN_AS_ROOT=1\n        export OMPI_ALLOW_RUN_AS_ROOT_CONFIRM=1\n        cd build-debug\n        # skip a number of expensive verification tests:\n        ctest --output-on-failure -j2 -VV -E \\\n          \"(verification-isentropic_vortex.*l7|verification-becker_solution.*l7)\"\n"
  },
  {
    "path": ".gitignore",
    "content": "build*\n.cache\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"simd-math\"]\n\tpath = simd-math\n\turl = https://github.com/vectorclass/version2\n"
  },
  {
    "path": ".mailmap",
    "content": "Martin Kronbichler <martin.kronbichler@uni-a.de>\nMartin Kronbichler <martin.kronbichler@uni-a.de> <kronbichler@lnm.mw.tum.de>\nMatthias Maier     <tamiko@43-1.org>             <maier@math.tamu.edu>\nIgnacio Tomas      <nachotet@gmail.com>\nIgnacio Tomas      <nachotet@gmail.com>          <itomas@tamu.edu>\nIgnacio Tomas      <nachotet@gmail.com>          <47006763+nachosaurus@users.noreply.github.com>\nEric Tovar         <ejtovar1@gmail.com>\nEric Tovar         <ejtovar1@gmail.com>          <ejtovar@math.tamu.edu>\nEric Tovar         <ejtovar1@gmail.com>          <36754207+ejtovar@users.noreply.github.com>\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2020 - 2024 by the ryujin authors\n##\n\n#\n# Sanity check:\n#\n\nget_filename_component(_source \"${CMAKE_SOURCE_DIR}\" REALPATH)\nget_filename_component(_build  \"${CMAKE_BINARY_DIR}\" REALPATH)\nif(\"${_source}\" STREQUAL \"${_build}\")\n  message(FATAL_ERROR\n    \"\\nRefusing to configure the project in the source directory. This \"\n    \"operation would globber important files. You need to configure in a \"\n    \"separate build directory. It is easiest to simply invoke $ make, which \"\n    \"will configure ryujin in the directory ./build.\\n\"\n    \"You will need to clean up ./CMakeCache.txt and ./CMakeFiles by hand, \"\n    \"or by running $ make cleanup_insource which will delete these files \"\n    \"and directories for you\"\n    )\nendif()\n\n#\n# Set up deal.II\n#\n\nset(RYUJIN_VERSION 2.1.1)\n\ncmake_minimum_required(VERSION 3.15)\nset(CMAKE_EXPORT_COMPILE_COMMANDS 1)\nlist(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)\n\nfind_package(deal.II 9.5 REQUIRED HINTS ${DEAL_II_DIR} $ENV{DEAL_II_DIR})\n\nset(CMAKE_BUILD_TYPE \"Release\" CACHE STRING\n  \"Choose the type of build, options are: Debug, Release\"\n  )\ndeal_ii_initialize_cached_variables()\ndeal_ii_query_git_information()\n\n# Work around a typo in the deal.II CMake configuration:\nif(EXISTS ${DEAL_II_GIT_CONFIG})\n  include(${DEAL_II_GIT_CONFIG})\nelse()\n  string(REPLACE\n    \"Git.cmake\" \"ConfigGit.cmake\" DEAL_II_GIT_CONFIG \"${DEAL_II_GIT_CONFIG}\"\n    )\n  if(EXISTS ${DEAL_II_GIT_CONFIG})\n    include(${DEAL_II_GIT_CONFIG})\n  endif()\nendif()\n\nmessage(STATUS \"  dealii: ${DEAL_II_GIT_REVISION}\")\nmessage(STATUS \"  ryujin: ${GIT_REVISION}\")\n\nif(NOT DEAL_II_WITH_MPI OR NOT DEAL_II_WITH_P4EST)\n  message(FATAL_ERROR\n    \"Need a deal.II library with mpi and p4est support enabled.\"\n    )\nendif()\n\nproject(ryujin CXX)\n\n#\n# Compile-time options:\n#\n\nset(NUMBER \"double\" CACHE STRING \"The principal floating point type\")\n\noption(DEBUG_EXPENSIVE_BOUNDS_CHECK \"Debug: enable debug code paths for additional limiter bounds checks\" OFF)\noption(DEBUG_OUTPUT \"Debug: enable detailed time-step output\" OFF)\noption(DEBUG_SANITIZER \"Debug: enable address and UBSAN sanitizers for DEBUG build\" OFF)\noption(DEBUG_SYMMETRY_CHECK \"Debug: enable debug code paths that verify d_ij, c_ij, m_ij for (anti)symmetry\" OFF)\noption(DEFAULT_ENABLE_EQUATIONS \"Default enable all available equations. This parameter only sets the default value of all EQUATION_* configuration options\" ON)\noption(DENORMALS_ARE_ZERO \"Set the \\\"denormals are zero\\\" and \\\"flush to zero\\\" bits in the MXCSR register\" ON)\n\noption(WITH_DEAL_II_THREADS \"Enable deal.II threading support\" OFF)\n\n#\n# External packages:\n#\n\noption(WITH_DOXYGEN \"Build documentation with doxygen\" OFF)\noption(WITH_LIKWID \"Compile and link against the likwid instrumentation library\" OFF)\n\nif(\"${WITH_OPENMP}\" STREQUAL \"\")\n  find_package(OpenMP QUIET)\nendif()\noption(WITH_OPENMP \"Enable threading support via OpenMP\" ${OpenMP_FOUND})\n\nif(\"${WITH_EOSPAC}\" STREQUAL \"\")\n  find_package(EOSPAC QUIET)\nendif()\noption(WITH_EOSPAC \"Add support for the EOSPAC suite\" ${EOSPAC_FOUND})\n\nif(\"${WITH_GDAL}\" STREQUAL \"\")\n  find_package(GDAL QUIET)\nendif()\noption(WITH_GDAL \"Compile and link against the gdal library\" ${GDAL_FOUND})\n\n#\n# Set up compiler flags:\n#\n\n# We require at least C++20:\nset(CMAKE_CXX_STANDARD 20)\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\nset(CMAKE_CXX_EXTENSIONS OFF)\n\nif(CMAKE_CXX_COMPILER_ID MATCHES \"GNU\")\n  string(APPEND DEAL_II_CXX_FLAGS \" -fdiagnostics-color=always\")\n  string(REPLACE \"-Qunused-arguments\" \"\" DEAL_II_CXX_FLAGS \"${DEAL_II_CXX_FLAGS}\")\n  string(REPLACE \"-Qunused-arguments\" \"\" DEAL_II_WARNING_FLAGS \"${DEAL_II_WARNING_FLAGS}\")\n  string(REPLACE \"-ffp-exception-behavior=strict\" \"\" DEAL_II_CXX_FLAGS_DEBUG \"${DEAL_II_CXX_FLAGS_DEBUG}\")\n  string(APPEND DEAL_II_CXX_FLAGS \" -Wno-missing-braces -Wno-unknown-pragmas\")\n\n  if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.0)\n    string(APPEND DEAL_II_CXX_FLAGS \" -Wno-overloaded-virtual\")\n  endif()\n\n  if(\"${NUMBER}\" STREQUAL \"float\")\n    string(APPEND DEAL_II_CXX_FLAGS \" -Wno-float-conversion\")\n  endif()\nendif()\n\nif(CMAKE_CXX_COMPILER_ID MATCHES \"Clang\")\n  string(APPEND DEAL_II_CXX_FLAGS \" -Xclang -fcolor-diagnostics\")\n\n  #\n  # This is finally fixed in clang-16 (by updating the C++20 support to the\n  # latest defect correction). For earlier versions, silence the warning.\n  #\n  if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS \"16.0.0\")\n    string(APPEND DEAL_II_CXX_FLAGS \" -Wno-ambiguous-reversed-operator\")\n  endif()\nendif()\n\nif(DEBUG_SANITIZER)\n  string(APPEND DEAL_II_CXX_FLAGS_DEBUG\n    \" -fsanitize=address,undefined,leak -fsanitize-address-use-after-return=always -fsanitize-address-use-after-scope\"\n    )\n  string(APPEND DEAL_II_LINKER_FLAGS_DEBUG\n    \" -fsanitize=address,undefined,leak -fsanitize-address-use-after-return=always -fsanitize-address-use-after-scope\"\n    )\nendif()\n\n# Respect user overrides performed via CMAKE_CXX_FLAGS/CMAKE_EXE_LINKER_FLAGS\nstring(APPEND DEAL_II_CXX_FLAGS \" $ENV{CXXFLAGS} ${CMAKE_CXX_FLAGS}\")\n\n#\n# Workaround: Remove any unwanted -std=c++17 flags that might be set in\n# DEAL_II_CXX_FLAGS. This is unfortunately the case for some deal.II\n# versions packaged in Debian and Ubuntu.\n#\nstring(REPLACE \"-std=c++17\" \"\" DEAL_II_CXX_FLAGS \"${DEAL_II_CXX_FLAGS}\")\n\nstring(APPEND DEAL_II_CXX_FLAGS_DEBUG \" ${CMAKE_CXX_FLAGS_DEBUG}\")\nstring(APPEND DEAL_II_CXX_FLAGS_RELEASE \" ${CMAKE_CXX_FLAGS_RELEASE}\")\nstring(APPEND DEAL_II_LINKER_FLAGS \" $ENV{LDFLAGS} ${CMAKE_EXE_LINKER_FLAGS}\")\nset(CMAKE_CXX_FLAGS \"\")\nset(CMAKE_CXX_FLAGS_RELEASE \"\")\nset(CMAKE_CXX_FLAGS_DEBUG \"\")\nset(CMAKE_EXE_LINKER_FLAGS \"\")\n\n#\n# Feature configuration:\n#\n\nset(EXTERNAL_TARGETS)\n\nif(WITH_EOSPAC)\n  find_package(EOSPAC REQUIRED)\n  list(APPEND EXTERNAL_TARGETS \"Eospac::Eospac6\")\nendif()\n\nif(WITH_GDAL)\n  find_package(GDAL REQUIRED)\n  list(APPEND EXTERNAL_TARGETS \"GDAL::GDAL\")\nendif()\n\nif(WITH_LIKWID)\n  find_package(LIKWID REQUIRED)\n  list(APPEND EXTERNAL_TARGETS \"Likwid::Likwid\")\nendif()\n\nif(WITH_OPENMP)\n  find_package(OpenMP REQUIRED)\n  list(APPEND EXTERNAL_TARGETS \"OpenMP::OpenMP_CXX\")\nendif()\n\n#\n# Set up the rest:\n#\n\ninclude(GNUInstallDirs)\n\nadd_subdirectory(source)\n\ninstall(FILES COPYING.md README.md\n  DESTINATION ${CMAKE_INSTALL_DOCDIR}\n  )\n\nfile(MAKE_DIRECTORY \"${CMAKE_BINARY_DIR}/run/\")\nfile(CREATE_LINK \"${CMAKE_SOURCE_DIR}/prm\" \"${CMAKE_BINARY_DIR}/run/prm\" SYMBOLIC)\ninstall(DIRECTORY prm DESTINATION ${CMAKE_INSTALL_DOCDIR})\n\nenable_testing()\nadd_subdirectory(tests)\n\nIF(WITH_DOXYGEN)\n  add_subdirectory(doc)\nENDIF()\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "Contributing to ryujin\n======================\n\nAs a contributor to this project, you agree that all of your contributions\nbe governed by the\n[Developer Certificate of Origin version 1.1](https://developercertificate.org/),\nsee [COPYING.md](COPYING.md) for details.\n"
  },
  {
    "path": "COPYING.md",
    "content": "ryujin copyright and license\n============================\n\nThis project is licensed under the the terms of the\n[Apache License 2.0](https://spdx.org/licenses/Apache-2.0.html) with\n[LLVM Exception](https://spdx.org/licenses/LLVM-exception.html).\n\nAs a contributor to this project, you agree that all of your contributions\nbe governed by the\n[Developer Certificate of Origin version 1.1](https://developercertificate.org/).\n\n\nThird party software\n--------------------\n\nThis program depends on and interfaces with the [deal.II finite element\nlibrary](https://dealii.org) which is dual licensed under the terms of the\nApache License 2.0 with LLVM Exception and the\n[GNU Lesser General Public License version 2.1 or later](https://spdx.org/licenses/LGPL-2.1-or-later.html).\n\nThe git submodule `simd-math/` contains the\n[vector class SIMD library](https://github.com/vectorclass/version2), which\nis third party software licensed under the terms of the Apache License 2.0\nand copyrighted by Agner Fog and collaborators.\n\n\nLogo\n----\n\nThe logo `doc/logo.png` shows a cropped portion of the painting \"The\nDragon\" by [Kunisada II Utagawa](https://en.wikipedia.org/wiki/Utagawa_Kunisada_II)\nwhich is in the public domain. The Wikimedia entry can be found\n[here](https://commons.wikimedia.org/wiki/File:Kunisada_II_The_Dragon.jpg).\n\n\nContact\n-------\n\nFor questions regarding licensing and commercial use please contact\nMatthias Maier <maier@tamu.edu>.\n\n\nFull license texts\n==================\n\nThe open source license that applies to a source file is annotated in the\nheader with an [SPDX identifier](https://spdx.dev/ids/). In addition, part\nof the source code (identified in the copyright header) is subject to a\nLANL Copyright Statement outlined below.\n\n\n[Developer Certificate of Origin version 1.1](https://developercertificate.org/)\n--------------------------------------------------------------------------------\n\n```\nDeveloper Certificate of Origin\nVersion 1.1\n\nCopyright (C) 2004, 2006 The Linux Foundation and its contributors.\n1 Letterman Drive\nSuite D4700\nSan Francisco, CA, 94129\n\nEveryone is permitted to copy and distribute verbatim copies of this\nlicense document, but changing it is not allowed.\n\n\nDeveloper's Certificate of Origin 1.1\n\nBy making a contribution to this project, I certify that:\n\n(a) The contribution was created in whole or in part by me and I\n    have the right to submit it under the open source license\n    indicated in the file; or\n\n(b) The contribution is based upon previous work that, to the best\n    of my knowledge, is covered under an appropriate open source\n    license and I have the right under that license to submit that\n    work with modifications, whether created in whole or in part\n    by me, under the same open source license (unless I am\n    permitted to submit under a different license), as indicated\n    in the file; or\n\n(c) The contribution was provided directly to me by some other\n    person who certified (a), (b) or (c) and I have not modified\n    it.\n\n(d) I understand and agree that this project and the contribution\n    are public and that a record of the contribution (including all\n    personal information I submit with it, including my sign-off) is\n    maintained indefinitely and may be redistributed consistent with\n    this project or the open source license(s) involved.\n```\n\n\n[Apache License 2.0](https://spdx.org/licenses/Apache-2.0.html)\n---------------------------------------------------------------\n\n```\n                             Apache License\n                       Version 2.0, January 2004\n                    http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n  \"License\" shall mean the terms and conditions for use, reproduction,\n  and distribution as defined by Sections 1 through 9 of this document.\n\n  \"Licensor\" shall mean the copyright owner or entity authorized by\n  the copyright owner that is granting the License.\n\n  \"Legal Entity\" shall mean the union of the acting entity and all\n  other entities that control, are controlled by, or are under common\n  control with that entity. For the purposes of this definition,\n  \"control\" means (i) the power, direct or indirect, to cause the\n  direction or management of such entity, whether by contract or\n  otherwise, or (ii) ownership of fifty percent (50%) or more of the\n  outstanding shares, or (iii) beneficial ownership of such entity.\n\n  \"You\" (or \"Your\") shall mean an individual or Legal Entity\n  exercising permissions granted by this License.\n\n  \"Source\" form shall mean the preferred form for making modifications,\n  including but not limited to software source code, documentation\n  source, and configuration files.\n\n  \"Object\" form shall mean any form resulting from mechanical\n  transformation or translation of a Source form, including but\n  not limited to compiled object code, generated documentation,\n  and conversions to other media types.\n\n  \"Work\" shall mean the work of authorship, whether in Source or\n  Object form, made available under the License, as indicated by a\n  copyright notice that is included in or attached to the work\n  (an example is provided in the Appendix below).\n\n  \"Derivative Works\" shall mean any work, whether in Source or Object\n  form, that is based on (or derived from) the Work and for which the\n  editorial revisions, annotations, elaborations, or other modifications\n  represent, as a whole, an original work of authorship. For the purposes\n  of this License, Derivative Works shall not include works that remain\n  separable from, or merely link (or bind by name) to the interfaces of,\n  the Work and Derivative Works thereof.\n\n  \"Contribution\" shall mean any work of authorship, including\n  the original version of the Work and any modifications or additions\n  to that Work or Derivative Works thereof, that is intentionally\n  submitted to Licensor for inclusion in the Work by the copyright owner\n  or by an individual or Legal Entity authorized to submit on behalf of\n  the copyright owner. For the purposes of this definition, \"submitted\"\n  means any form of electronic, verbal, or written communication sent\n  to the Licensor or its representatives, including but not limited to\n  communication on electronic mailing lists, source code control systems,\n  and issue tracking systems that are managed by, or on behalf of, the\n  Licensor for the purpose of discussing and improving the Work, but\n  excluding communication that is conspicuously marked or otherwise\n  designated in writing by the copyright owner as \"Not a Contribution.\"\n\n  \"Contributor\" shall mean Licensor and any individual or Legal Entity\n  on behalf of whom a Contribution has been received by Licensor and\n  subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\n  this License, each Contributor hereby grants to You a perpetual,\n  worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n  copyright license to reproduce, prepare Derivative Works of,\n  publicly display, publicly perform, sublicense, and distribute the\n  Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\n  this License, each Contributor hereby grants to You a perpetual,\n  worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n  (except as stated in this section) patent license to make, have made,\n  use, offer to sell, sell, import, and otherwise transfer the Work,\n  where such license applies only to those patent claims licensable\n  by such Contributor that are necessarily infringed by their\n  Contribution(s) alone or by combination of their Contribution(s)\n  with the Work to which such Contribution(s) was submitted. If You\n  institute patent litigation against any entity (including a\n  cross-claim or counterclaim in a lawsuit) alleging that the Work\n  or a Contribution incorporated within the Work constitutes direct\n  or contributory patent infringement, then any patent licenses\n  granted to You under this License for that Work shall terminate\n  as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\n  Work or Derivative Works thereof in any medium, with or without\n  modifications, and in Source or Object form, provided that You\n  meet the following conditions:\n\n  (a) You must give any other recipients of the Work or\n      Derivative Works a copy of this License; and\n\n  (b) You must cause any modified files to carry prominent notices\n      stating that You changed the files; and\n\n  (c) You must retain, in the Source form of any Derivative Works\n      that You distribute, all copyright, patent, trademark, and\n      attribution notices from the Source form of the Work,\n      excluding those notices that do not pertain to any part of\n      the Derivative Works; and\n\n  (d) If the Work includes a \"NOTICE\" text file as part of its\n      distribution, then any Derivative Works that You distribute must\n      include a readable copy of the attribution notices contained\n      within such NOTICE file, excluding those notices that do not\n      pertain to any part of the Derivative Works, in at least one\n      of the following places: within a NOTICE text file distributed\n      as part of the Derivative Works; within the Source form or\n      documentation, if provided along with the Derivative Works; or,\n      within a display generated by the Derivative Works, if and\n      wherever such third-party notices normally appear. The contents\n      of the NOTICE file are for informational purposes only and\n      do not modify the License. You may add Your own attribution\n      notices within Derivative Works that You distribute, alongside\n      or as an addendum to the NOTICE text from the Work, provided\n      that such additional attribution notices cannot be construed\n      as modifying the License.\n\n  You may add Your own copyright statement to Your modifications and\n  may provide additional or different license terms and conditions\n  for use, reproduction, or distribution of Your modifications, or\n  for any such Derivative Works as a whole, provided Your use,\n  reproduction, and distribution of the Work otherwise complies with\n  the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\n  any Contribution intentionally submitted for inclusion in the Work\n  by You to the Licensor shall be under the terms and conditions of\n  this License, without any additional terms or conditions.\n  Notwithstanding the above, nothing herein shall supersede or modify\n  the terms of any separate license agreement you may have executed\n  with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\n  names, trademarks, service marks, or product names of the Licensor,\n  except as required for reasonable and customary use in describing the\n  origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\n  agreed to in writing, Licensor provides the Work (and each\n  Contributor provides its Contributions) on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n  implied, including, without limitation, any warranties or conditions\n  of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n  PARTICULAR PURPOSE. You are solely responsible for determining the\n  appropriateness of using or redistributing the Work and assume any\n  risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\n  whether in tort (including negligence), contract, or otherwise,\n  unless required by applicable law (such as deliberate and grossly\n  negligent acts) or agreed to in writing, shall any Contributor be\n  liable to You for damages, including any direct, indirect, special,\n  incidental, or consequential damages of any character arising as a\n  result of this License or out of the use or inability to use the\n  Work (including but not limited to damages for loss of goodwill,\n  work stoppage, computer failure or malfunction, or any and all\n  other commercial damages or losses), even if such Contributor\n  has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\n  the Work or Derivative Works thereof, You may choose to offer,\n  and charge a fee for, acceptance of support, warranty, indemnity,\n  or other liability obligations and/or rights consistent with this\n  License. However, in accepting such obligations, You may act only\n  on Your own behalf and on Your sole responsibility, not on behalf\n  of any other Contributor, and only if You agree to indemnify,\n  defend, and hold each Contributor harmless for any liability\n  incurred by, or claims asserted against, such Contributor by reason\n  of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\n  To apply the Apache License to your work, attach the following\n  boilerplate notice, with the fields enclosed by brackets \"[]\"\n  replaced with your own identifying information. (Don't include\n  the brackets!)  The text should be enclosed in the appropriate\n  comment syntax for the file format. We also recommend that a\n  file or class name and description of purpose be included on the\n  same \"printed page\" as the copyright notice for easier\n  identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n\n\n[LLVM Exception](https://spdx.org/licenses/LLVM-exception.html)\n---------------------------------------------------------------\n\n```\nAs an exception, if, as a result of your compiling your source code, portions\nof this Software are embedded into an Object form of such source code, you\nmay redistribute such embedded portions in such Object form without complying\nwith the conditions of Sections 4(a), 4(b) and 4(d) of the License.\n\nIn addition, if you combine or link compiled forms of this Software with\nsoftware that is licensed under the GPLv2 (\"Combined Software\") and if a\ncourt of competent jurisdiction determines that the patent provision (Section\n3), the indemnity provision (Section 9) or other Section of the License\nconflicts with the conditions of the GPLv2, you may retroactively and\nprospectively choose to deem waived or otherwise exclude such Section(s) of\nthe License, but only in their entirety and only with respect to the Combined\nSoftware.\n```\n\n\nLANL Copyright Statement\n------------------------\n\nThe following statement applies to a number of contributed source code\nfiles under `./source` which are identified by the string\n`[LANL Copyright Statement]` in the copyright header:\n```\nCopyright (c) 2023 - 2024 by Triad National Security, LLC\n\nAll rights reserved.\n\nThis program was produced under U.S. Government contract 89233218CNA000001\nfor Los Alamos National Laboratory (LANL), which is operated by Triad\nNational Security, LLC for the U.S. Department of Energy/National Nuclear\nSecurity Administration. All rights in the program are reserved by Triad\nNational Security, LLC, and the U.S. Department of Energy/National Nuclear\nSecurity Administration. The Government is granted for itself and others\nacting on its behalf a nonexclusive, paid-up, irrevocable worldwide license\nin this material to reproduce, prepare derivative works, distribute copies\nto the public, perform publicly and display publicly, and to permit others\nto do so.\n\nThis program is Open-Source under the Apache 2.0 License.\n```\n"
  },
  {
    "path": "INSTALLATION.md",
    "content": "Installation instructions\n=========================\n\nNecessary tools and library dependencies\n----------------------------------------\n\nryujin requires [deal.II](https://dealii.org) version 9.4.0 or newer\ncompiled with support enabled for MPI and P4est.\n\nOn Debian and Ubuntu you can conveniently install all necessary libraries\nand tools with `apt`.\n  - On <b>Debian</b> you can run the following command (as root user) to\n    install deal.II, all development libraries and necessary tools:\n    ```\n    apt install libdeal.ii-dev cmake cmake-curses-gui numdiff make g++ ninja-build git\n    ```\n  - On <b>Ubuntu LTS 24.04</b> you can run the following command to install\n    deal.II, all development libraries and necessary tools:\n    ```\n    sudo apt install libdeal.ii-dev cmake cmake-curses-gui numdiff make g++ ninja-build git\n    ```\n  - On <b>Ubuntu LTS 22.04</b> you can install the current deal.II version\n    from a <a href=\"https://launchpad.net/~ginggs/+archive/ubuntu/deal.ii-9.5.1-backports\">PPA</a>\n    ```\n    sudo add-apt-repository ppa:ginggs/deal.ii-9.5.1-backports\n    sudo apt update\n    sudo apt install libdeal.ii-dev cmake cmake-curses-gui numdiff make g++ ninja-build git\n    ```\n\nIf you are on a MAC it might be easiest to simply install our precompiled\nMAC bundle for deal.II which you can find on the [Github download\npage](https://github.com/dealii/dealii/releases/tag/v9.5.2).\n\nIf you are on Windows we strongly recommend to set up the [<b>Windows\nSubsystem for Linux v2</b>\n(WSL)](https://learn.microsoft.com/en-us/windows/wsl/about) and install\n<b>Ubuntu LTS 24.04</b> from the Microsoft store. Then, simply launch the\nUbuntu App which starts a Bash shell. You can then proceed with the\ninstallation instructions for Ubuntu LTS 24.04 summarized above. You can\nfind a helpful tutorial on how to use the linux command line\n[here](https://ubuntu.com/tutorials/command-line-for-beginners#1-overview).\n\nGeneric installation instructions how to download, install, or manually\ncompile deal.II can be found on the\n[deal.II homepage](https://dealii.org/download.html) and on the\n[deal.II wiki](https://github.com/dealii/dealii/wiki).\nMake sure that deal.II is configured with MPI and P4est support and that\nthe following additional tools are in your `PATH`: git, numdiff, cmake,\nmake.\n\n\nRetrieving and compiling ryujin\n-------------------------------\n\nAt this point you should have deal.II and all necessary command line tools\ninstalled. You then simply check out the ryujin repository and run `make`\nto compile ryujin:\n```\ngit clone https://github.com/conservation-laws/ryujin\ncd ryujin\ngit submodule init\ngit submodule update\nmake release\n```\n\nNote that ryujin uses the [CMake](https://cmake.org/) build system. The\n`Makefile` found in the repository only contains a number of convenience\ntargets and its use is entirely optional. If invoked it will create a\nsubdirectory `build`, run cmake to configure the project, and make to build\nit. The executable will be located in <code>build/run</code>. The\nconvenience Makefile contains the following additional targets:\n  - `make debug`:  switch to debug build and compile program\n  - `make release`:  switch to release build and compile program\n  - `make edit_cache`:  runs ccmake in the build directory\n"
  },
  {
    "path": "Makefile",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2020 - 2023 by the ryujin authors\n##\n\nSHELL:=bash\ndefault: all\n\n.PHONY: default\n\nSOURCEDIR:=$(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))\nBUILDDIR:=build\n\nifeq (, $(shell which ninja))\n  GENERATOR:=\"Unix Makefiles\"\n  MAKE_COMMAND:=make\n  MAKE_FILE:=Makefile\nelse\n  GENERATOR:=Ninja\n  MAKE_COMMAND:=ninja\n  MAKE_FILE:=build.ninja\nendif\n\n##########################################################################\n\ncleanup_insource:\n\t@rm -f $(SOURCEDIR)/CMakeCache.txt\n\t@rm -rf $(SOURCEDIR)/CMakeFiles\n\nrebuild_cache:\n\t@mkdir -p $(BUILDDIR)\n\t@cd $(BUILDDIR) && cmake -G$(GENERATOR) $(SOURCEDIR)\n\nedit_cache:\n\t@mkdir -p $(BUILDDIR)\n\t@cd $(BUILDDIR) && ccmake -G$(GENERATOR) $(SOURCEDIR)\n\n$(BUILDDIR)/$(MAKE_FILE):\n\t@mkdir -p $(BUILDDIR)\n\t@cd $(BUILDDIR) && cmake -G$(GENERATOR) $(SOURCEDIR)\n\ndebug:\n\t@mkdir -p $(BUILDDIR)\n\t@cd $(BUILDDIR) && cmake -DCMAKE_BUILD_TYPE=Debug -G$(GENERATOR) $(SOURCEDIR)\n\t@cd $(BUILDDIR) && $(MAKE_COMMAND)\n\nrelease:\n\t@mkdir -p $(BUILDDIR)\n\t@cd $(BUILDDIR) && cmake -DCMAKE_BUILD_TYPE=Release -G$(GENERATOR) $(SOURCEDIR)\n\t@cd $(BUILDDIR) && $(MAKE_COMMAND)\n\nMakefile:\n\t\n\n%: cleanup_insource $(BUILDDIR)/$(MAKE_FILE)\n\t@cd $(BUILDDIR) && $(MAKE_COMMAND) $@\n\n.PHONY: cleanup_insource rebuild_cache edit_cache debug release\n\n##########################################################################\n\nindent:\n\t@clang-format -i source/*.h source/*.cc source/**/*.h source/**/*.cc tests/**/*.cc\n\n.PHONY: indent\n\n##########################################################################\n"
  },
  {
    "path": "README.md",
    "content": "<img align=\"right\" height=\"150\" src=\"doc/logo.png\">\n\nryujin\n======\n\nRyujin is a high-performance high-order collocation-type finite-element\nsolver for conservation equations such as the compressible Navier-Stokes\nand Euler equations of gas dynamics. The solver is based on the [convex\nlimiting technique](https://doi.org/10.1137/17M1149961) to ensure\n[invariant domain preservation](https://doi.org/10.1137/16M1074291) and\nuses the finite element library [deal.II](https://github.com/dealii/dealii)\n([website](https://www.dealii.org)) and the [vector class SIMD\nlibrary](https://github.com/vectorclass/version2). As such the solver\nmaintains important physical invariants and is guaranteed to be stable\nwithout the use of ad-hoc tuning parameters.\n\nRyujin is freely available under the terms of the\n[Apache License 2.0](https://spdx.org/licenses/Apache-2.0.html)\nwith [LLVM Exception](https://spdx.org/licenses/LLVM-exception.html).\nPart of the contributed source code, third-party dependencies and header\nlibraries are covered by different open source licenses. For details\nconsult [COPYING.md](COPYING.md). Contributions to the ryujin source code\nare governed by the [Developer Certificate of Origin version\n1.1](https://developercertificate.org/); see\n[CONTRIBUTING.md](CONTRIBUTING.md) for details.\n\n\nModules\n-------\n\nRyujin features the following equation modules selectable by the following\nparameter flags:\n - `equation = euler`, an optimized solver module for the\n   [compressible Euler\n   equations](https://en.wikipedia.org/wiki/Euler_equations_(fluid_dynamics))\n   with polytropic equation of state.\n - `equation = euler barotropic`, an optimized solver module for the\n   [compressible Euler\n   equations](https://en.wikipedia.org/wiki/Euler_equations_(fluid_dynamics))\n   with a barotropic equation of state, such as the isentropic or\n   isothermal equation of state.\n - `equation = euler aeos`, a generalized solver module for the\n   compressible Euler equation with an [arbitrary or tabulated equation of\n   state](https://en.wikipedia.org/wiki/Equation_of_state).\n - `equation = navier stokes`, an optimized solver module for the\n   [compressible Navier-Stokes\n   equations](https://en.wikipedia.org/wiki/Navier%E2%80%93Stokes_equations)\n   with polytropic equation of state,\n   Newtonian fluid model, and Fourier's law for the heat flux.\n - `equation = euler poisson`, an optimized solver module for a charged\n   (single species) fluid goverened by the compressible Euler with\n   polytropic gas equation of state, and subject to an electrostatic force\n   model with background magnetic field.\n - `equation = euler poisson barotropic`, an optimized solver module for a charged\n   (single species) fluid goverened by the compressible Euler\n   equations](https://en.wikipedia.org/wiki/Euler_equations_(fluid_dynamics))\n   with a barotropic equation of state, and subject to an electrostatic\n   force model with background magnetic field.\n - `equation = euler poisson aeos`, an generalized solver module for a charged\n   (single species) fluid goverened by the compressible Euler equation with\n   an arbitrary or tabulated equation of state, and subject to an\n   electrostatic force model with background magnetic field.\n - `equation = shallow water`, a module for the [shallow water\n   equations](https://en.wikipedia.org/wiki/Shallow_water_equations).\n - `equation = scalar conservation`, a module for scalar conservation\n   equations with user-supplied flux. The module features a greedy\n   wave-speed estimate to maintain an invariant domain, a generic indicator\n   based on the entropy-viscosity commutator technique with a general,\n   entropy-like function, and a customizable convex limiter.\n\nResources\n---------\n\n - [Website](https://conservation-laws.org/)\n - [Installation instructions](./INSTALLATION.md)\n - [Usage instructions](./USAGE.md)\n - [Doxygen documentation](https://conservation-laws.org/ryujin/doxygen)\n\nVideos\n------\n\nA number of simulation results can be found on [this youtube\nchannel](https://www.youtube.com/@matthiasmaier8956).\n\n[<img src=\"https://img.youtube.com/vi/ig7R3yA7CtE/maxresdefault.jpg\" width=\"400\"/>](https://www.youtube.com/watch?v=ig7R3yA7CtE)\n[<img src=\"https://img.youtube.com/vi/yM2rT3teakE/maxresdefault.jpg\" width=\"400\"/>](https://www.youtube.com/watch?v=yM2rT3teakE)\n[<img src=\"https://img.youtube.com/vi/xIwJZlsXpZ4/0.jpg\" width=\"400\"/>](https://www.youtube.com/watch?v=xIwJZlsXpZ4)\n[<img src=\"https://img.youtube.com/vi/pPP26zelb0M/0.jpg\" width=\"400\"/>](https://www.youtube.com/watch?v=pPP26zelb0M)\n[<img src=\"https://img.youtube.com/vi/vBCRAF_c8m8/0.jpg\" width=\"400\"/>](https://www.youtube.com/watch?v=vBCRAF_c8m8)\n[<img src=\"https://img.youtube.com/vi/xecIZylotSE/0.jpg\" width=\"400\"/>](https://www.youtube.com/watch?v=xecIZylotSE)\n\nThere original videos are available\n[here](https://tamiko.43-1.org/developer/) ([license\ndetails](https://tamiko.43-1.org/developer/COPYING.txt)).\n\nReferences\n----------\n\nIf you use this software for an academic publication please consider citing\nsome of the following references:\n\n```\n@article{ryujin-2021-1,\n  author  = {Matthias Maier and Martin Kronbichler},\n  title   = {Efficient parallel 3D computation of the compressible Euler equations with an invariant-domain preserving second-order finite-element scheme},\n  doi     = {10.1145/3470637},\n  url     = {https://arxiv.org/abs/2007.00094},\n  journal = {ACM Transactions on Parallel Computing},\n  year    = {2021},\n  volume  = {8},\n  number  = {3},\n  pages   = {16:1-30}\n}\n\n@article{ryujin-2021-2,\n  author  = {Jean-Luc Guermond and Matthias Maier and Bojan Popov and Ignacio Tomas},\n  title   = {Second-order invariant domain preserving approximation of the compressible Navier--Stokes equations},\n  doi     = {10.1016/j.cma.2020.113608},\n  url     = {https://arxiv.org/abs/2009.06022},\n  journal = {Computer Methods in Applied Mechanics and Engineering},\n  year    = {2021},\n  volume  = {375},\n  number  = {1},\n  pages   = {113608}\n}\n\n@article{ryujin-2021-3,\n  author  = {Jean-Luc~Guermond and Martin Kronbichler and Matthias Maier and Bojan Popov and Ignacio Tomas},\n  title   = {On the implementation of a robust and efficient finite element-based parallel solver for the compressible Navier-stokes equations},\n  url     = {https://arxiv.org/abs/2106.02159},\n  journal = {Computer Methods in Applied Mechanics and Engineering},\n  year    = {2022},\n  volume  = {389},\n  pages   = {114250}\n}\n\n@article{ryujin-2023-4,\n  author  = {Bennett Clayton and Jean-Luc Guermond and Matthias Maier and Bojan Popov and Tovar, Eric J.},\n  title   = {Robust second-order approximation of the compressible Euler equations with an arbitrary equation of state},\n  url     = {http://arxiv.org/abs/2207.12832},\n  journal = {Journal of Computational Physics},\n  pages   = {111926},\n  year    = {2023}\n}\n\n@article{ryujin-2024-5,\n  author = {Jean-Luc Guermond and Matthias Maier and Bojan Popov and Laura Saavedra and Ignacio Tomas},\n  title = {First-Order Greedy Invariant-Domain Preserving Approximation for Hyperbolic Problems: Scalar Conservation Laws, and p-System},\n  url = {https://arxiv.org/abs/2310.01713},\n  journal = {Journal of Scientific Computing},\n  year = {2024},\n  volume = {100},\n  number = {46},\n  pages = {},\n}\n\n@article{ryujin-2025-6,\n  author = {Jean-Luc Guermond and Matthias Maier and Tovar, Eric J.},\n  title = {A high-order explicit Runge-Kutta approximation technique for the shallow water equations},\n  url = {https://arxiv.org/abs/2403.17123},\n  journal = {Computers \\& Fluids},\n  year = {2025},\n  volume = {288},\n  pages = {106493},\n}\n\n@article{ryujin-2025-7,\n  author = {Martin Kronbichler and Matthias Maier and Ignacio Tomas},\n  title = {Graph-based methods for hyperbolic systems of conservation laws using discontinuous space discretizations},\n  url = {https://arxiv.org/abs/2402.04514},\n  journal = {Communications in Computational Physics},\n  year = {2025},\n  volume = {38},\n  pages = {74--108}\n}\n\n@article{ryujin-2025-8,\n  author = {Jake Harmon and Martin Kronbichler and Matthias Maier and Eric Tovar},\n  title = {A conservative invariant-domain preserving projection technique for hyperbolic systems under adaptive mesh refinement},\n  url = {https://arxiv.org/abs/2507.18717},\n  year = {2025},\n  journal = {submitted}\n}\n\n@article{ryujin-2025-9,\n  author = {Jordan Hoffart and Matthias Maier and John Shadid and Ignacio Tomas},\n  title = {Structure-preserving finite-element approximations of the magnetic Euler-Poisson equations},\n  url = {https://arxiv.org/abs/2510.11808},\n  year = {2025},\n  journal = {submitted}\n}\n```\n\nContact\n-------\n\nFor questions either open an\n[issue](https://github.com/conservation-laws/ryujin/issues), or contact\nMatthias Maier (maier@tamu.edu).\n\nDevelopers\n----------\n\n - Martin Kronbichler ([@kronbichler](https://github.com/kronbichler)), Ruhr University Bochum, Germany\n - Matthias Maier ([@tamiko](https://github.com/tamiko)), Texas A&M University, TX, USA\n - Ignacio Tomas ([@nachosaurus](https://github.com/nachosaurus)), Texas Tech University, TX, USA\n - Eric Tovar ([@ejtovar](https://github.com/ejtovar)), Xcimer Energy Corporation, USA\n\nContributors\n------------\n\n - Wolfgang Bangerth [@bangerth](https://github.com/bangerth), Colorado State University\n - Taylor Boylan ([@tmboylan](https://github.com/tmboylan)),\n - Jerett Cherry ([@jerret-cc](https://github.com/jerett-cc)), Colorado State University\n - Gregory Christian ([@gregorychristian](https://github.com/gregorychristian), Imperial College London, UK\n - Bennett Clayton ([@bgclayto](https://github.com/bgclayto))\n - Seth Gerberding ([@Gerbeset](https://github.com/Gerbeset)), Texas A&M University, TX, USA\n - Jake Harmon ([@harmonj](https://github.com/harmonj)), Los Alamos National Laboratory, USA\n - Jordan Hoffart ([@jordanhoffart](https://github.com/jordanhoffart)), Texas A&M University, TX, USA\n - David Pecoraro ([@ChrisPec27](https://github.com/ChrisPec27)), Texas A&M University, TX, USA\n - Madison Sheridan ([@Helblindi](https://github.com/Helblindi)), Texas A&M University, TX, USA\n"
  },
  {
    "path": "USAGE.md",
    "content": "Usage Usage instructions\n========================\n\nConvenience Makefile\n--------------------\n\nThe `Makefile` found in the repository only contains a number of\nconvenience targets and its use is entirely optional. It will create a\nsubdirectory `build` and run cmake to configure the project. The executable\nwill be located in `build/run`. The convenience Makefile contains the\nfollowing additional targets:\n  - `make debug`:  switch to debug build and compile program\n  - `make release`:  switch to release build and compile program\n  - `make edit_cache`:  runs ccmake in the build directory\n\nExecutable and runtime parameter files\n--------------------------------------\n\nAfter compiling ryujin you should end up with an executable located in the\nbuild directory at `build/run/ryujin`. The executable takes a parameter\nfile location as single (optional) argument. If run without an argument it\nwill try to open a parameter file `ryujin.prm` in the working directory of\nthe executable (that is `build/run`):\n```\ncd build/run\n./ryujin # uses ryujin.prm\n./ryujin my_parameter_file.prm\n```\nYou can find a number of example parameter files in the `prm` subdirectory.\n\nSpecifically, the `prm/verification` subdirectory contains parameter files\nand <i>baseline</i> output vectors that document expected error and\nconvergence rates for various analytical solutions. All configurations\ncompare the simulation result at final time to a known analytic solution\nand record the normalized L1, L2, and L\\infty error norms (summed up over\nall components). All test configurations should be run with double floating\npoint precision.\n\nThe `prm/benchmarks` directory contains parameter files for well known and\npopular <i>benchmark</i> configurations. These configurations typically do\nnot have an analytical solution, but the expected solution structure is\nwell known. They are thus usually compared in the <i>eyeball norm</i>.\nFor example, to run the Mach 3 cylinder with 2.36M gridpoints on a machine\nwith 16 cores and two threads per core you could use:\n```\ncd build/run\nDEAL_II_NUM_THREADS=2 mpirun -np 16 ryujin prm/benchmarks/euler-mach3-cylinder-2d.prm\n```\nWarning: This is a rather large computation. For quick tests you might want\nto decrease the resolution by lowering the number set by `set mesh\nrefinement` in the prm file.\n\nYou can obtain a full list of supported runtime parameters and their\ndefault values by invoking the `./ryujin` executable without creating a\n`ryujin.prm` in the path:\n```\n% ./ryujin\n[INFO] initiating flux capacitor\n[INFO] Default parameter file »ryujin.prm« not found.\n[INFO] Creating template parameter files...\n% ls\n[...]\ndefault_parameters-euler-2d-description.prm\ndefault_parameters-navier_stokes-2d-description.prm\ndefault_parameters-shallow_water-2d-description.prm\ndefault_parameters-euler_aeos-2d-description.prm\ndefault_parameters-scalar_conservation-2d-description.prm\n```\nConsult the above parameter files with detailed annotated configuration\noptions (and their 1d and 3d counterparts) for details.\n\nOutput file format\n------------------\n\nryujin supports outputting temporal snapshots in `.vtu` format. (In\nprinciple you can modify ryujin to output any file format supported by\ndeal.II, have a look at `source/vtu_output.template.h`) You can use\nParaview to open and inspect `.vtu` files, which you can install via your\npackage manager or obtain [here](https://www.paraview.org/).\n\nryujin has some rudimentary support for outputting instantaneous, time\naveraged, or space integrated primitive values (and their second moments)\non user defined level sets.\n\nControlling parallelism and screen output\n-----------------------------------------\n\nryujin uses MPI and thread parallelization. You typically control the\ndegree of MPI parallelism by invoking ryujin with an MPI launcher program.\nThe number of threads created by each rank is controlled with the\n`DEAL_II_NUM_THREADS` environment variable. For example, you can run ryujin\non 8 ranks with 4 threads each as follows:\n```\nDEAL_II_NUM_THREADS=4 mpirun -np 8 ./ryujin\n```\n\n\nCompile time options\n--------------------\n\nryujin has a number of compile time options to fine tune certain behavior.\nYou can set and change compile time options by specifying them on the\ncommand line during configuration with `cmake`, or `ccmake`. You can invoke\n`ccmake` as follows:\n```\ncd build\nccmake .\n```\nYou can also invoke `ccmake` conveniently with the convenience `Makefile`\nfrom anywhere in the source directory by running\n```\nmake edit_cache\n```\n\nThe most important compile-time option is `CMAKE_BUILD_TYPE` that is used\nto switch between a debug build configuration (`Debug`) and a release build\nconfiguration (`Release`). This option can also be set quickly with the\nconvenience `Makefile` and typing `make debug`, or `make release` from the\ntop level directory of ryujin.\n\nChanging other compile-time options is rarely needed. And if in doubt they\nare best kept at their default values. For completeness we list all\nconfiguration options here:\n  - `CMAKE_BUILD_TYPE`: build ryujin in \"Release\" or \"Debug\" mode\n  - `NUMBER`: select \"double\" for double precision or \"float\" for single precision (defaults to double)\n  - `ASYNC_MPI_EXCHANGE`: enable asynchronous \"communication hiding\" MPI exchange (defaults to OFF)\n  - `DEBUG_EXPENSIVE_BOUNDS_CHECK`: enable debug code paths for additional limiter bounds checks (defaults to OFF)\n  - `DEBUG_OUTPUT`: enable debug output (defaults to OFF)\n  - `DEBUG_SANITIZER`: enable address and UBSAN sanitizers for DEBUG build (defaults to OFF)\n  - `DEBUG_SYMMETRY_CHECK`: Debug: enable debug code paths that verify d_ij, c_ij, m_ij for (anti)symmetry (defaults to OFF)\n  - `DENORMALS_ARE_ZERO`: disable floating point denormals (defaults to ON)\n  - `WITH_DOXYGEN`: enable support for doxygen and build documentation\n  - `WITH_EOSPAC`: enable support for the EOSPAC6/Sesame tabulated equation of state database (autodetection)\n  - `WITH_LIKWID`: enable support for Likwid stetoscope mode (library for Intel performance counters, defaults to OFF)\n  - `WITH_OPENMP`: enable support for multithreading via OpenMP (autodetection)\n"
  },
  {
    "path": "cmake/FindEOSPAC.cmake",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2023 by the ryujin authors\n##\n\n\n# FIXME: this should be made much more robust. But *meh*\n\nset(EOSPAC_LIBRARY \"\")\nforeach(_prefix ${CMAKE_PREFIX_PATH})\n  file(GLOB_RECURSE _candidates ${_prefix}/*/libeospac6.a)\n  if(NOT \"${_candidates}\" STREQUAL \"\")\n    list(GET _candidates 0 EOSPAC_LIBRARY)\n    break()\n  endif()\nendforeach()\nif(\"${EOSPAC_LIBRARY}\" STREQUAL \"\")\n  set(EOSPAC_LIBRARY \"EOSPAC_LIBRARY-NOTFOUND\")\nendif()\n\nset(EOSPAC_INCLUDE_DIR \"\")\nforeach(_prefix ${CMAKE_PREFIX_PATH})\n  file(GLOB_RECURSE _candidates ${_prefix}/*/eos_Interface.h)\n  if(NOT \"${_candidates}\" STREQUAL \"\")\n    list(GET _candidates 0 EOSPAC_INCLUDE_DIR)\n    get_filename_component(EOSPAC_INCLUDE_DIR \"${EOSPAC_INCLUDE_DIR}\" DIRECTORY)\n    break()\n  endif()\nendforeach()\nif(\"${EOSPAC_INCLUDE_DIR}\" STREQUAL \"\")\n  set(EOSPAC_INCLUDE_DIR \"EOSPAC_INCLUDE_DIR-NOTFOUND\")\nendif()\n\nfind_package_handle_standard_args(EOSPAC DEFAULT_MSG\n  EOSPAC_LIBRARY EOSPAC_INCLUDE_DIR\n  )\n\nif(EOSPAC_FOUND AND NOT TARGET Eospac::Eospac6)\n  add_library(Eospac::Eospac6 INTERFACE IMPORTED)\n  target_link_libraries(Eospac::Eospac6 INTERFACE ${EOSPAC_LIBRARY})\n  target_include_directories(Eospac::Eospac6 SYSTEM INTERFACE ${EOSPAC_INCLUDE_DIR})\nendif()\n"
  },
  {
    "path": "cmake/FindLIKWID.cmake",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2023 by the ryujin authors\n##\n\nfind_path(LIKWID_INCLUDE_DIR likwid.h\n  PATH_SUFFIXES include\n  )\n\nfind_library(LIKWID_LIBRARY\n  NAMES likwid\n  PATH_SUFFIXES lib${LIB_SUFFIX} lib64 lib\n  )\n\nfind_library(LIKWID_HWLOC_LIBRARY\n  NAMES likwid-hwloc\n  PATH_SUFFIXES lib${LIB_SUFFIX} lib64 lib\n  )\n\nfind_library(LIKWID_LUA_LIBRARY\n  NAMES likwid-lua\n  PATH_SUFFIXES lib${LIB_SUFFIX} lib64 lib\n  )\n\nfind_package_handle_standard_args(LIKWID DEFAULT_MSG\n  LIKWID_LIBRARY LIKWID_INCLUDE_DIR\n  )\n\nif(LIKWID_FOUND AND NOT TARGET Likdwid::Likwid)\n  add_library(Likwid::Likwid INTERFACE IMPORTED)\n  target_link_libraries(Likwid::Likwid INTERFACE ${LIKWID_LIBRARY})\n\n  foreach(_lib LIKWID_HWLOC_LIBRARY LIKWID_LUA_LIBRARY)\n    if(NOT \"${${_lib}}\" MATCHES \"-NOTFOUND\")\n      target_link_libraries(Likwid::Likwid INTERFACE ${${_lib}})\n    endif()\n  endforeach()\n\n  target_include_directories(Likwid::Likwid SYSTEM INTERFACE ${LIKWID_INCLUDE_DIR})\n  target_compile_definitions(Likwid::Likwid INTERFACE \"LIKWID_PERFMON\")\nendif()\n"
  },
  {
    "path": "doc/CMakeLists.txt",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2020 - 2023 by the ryujin authors\n##\n\nfind_package(Doxygen REQUIRED)\n\nfind_path(MATHJAX_PATH MathJax.js PATHS\n  ${MATHJAX_PATH} $ENV{MATHJAX_PATH}\n  /usr/share/mathjax /usr/share/javascript/mathjax\n  )\n\nif(MATHJAX_PATH MATCHES \"MATHJAX_PATH-NOTFOUND\")\n  set(MATHJAX_PATH \"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/\")\nendif()\n\nmessage(STATUS \"Using MathJax path: »${MATHJAX_PATH}«\")\n\nfile(GLOB_RECURSE DOXYGEN_INPUT\n  ${CMAKE_SOURCE_DIR}/source/*.h\n  ${CMAKE_SOURCE_DIR}/doc/headers/*.h\n  )\nstring(REPLACE \";\" \" \" DOXYGEN_INPUT \"${DOXYGEN_INPUT}\")\n\nfile(GLOB_RECURSE DOXYGEN_DEPEND\n  ${CMAKE_CURRENT_SOURCE_DIR}/options.dox.in\n  ${CMAKE_SOURCE_DIR}/source/*.h\n  ${CMAKE_SOURCE_DIR}/doc/headers/*.h\n  )\n\nconfigure_file(\n  ${CMAKE_CURRENT_SOURCE_DIR}/options.dox.in\n  ${CMAKE_CURRENT_BINARY_DIR}/options.dox\n  @ONLY\n  )\n\nadd_custom_command(\n  OUTPUT ${CMAKE_BINARY_DIR}/doxygen.log\n  COMMAND ${DOXYGEN_EXECUTABLE}\n    ${CMAKE_CURRENT_BINARY_DIR}/options.dox\n    > ${CMAKE_BINARY_DIR}/doxygen.log 2>&1\n  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}\n  DEPENDS\n  ${DOXYGEN_DEPEND}\n  COMMENT \"Generating documentation via doxygen.\"\n  VERBATIM\n  )\n\nadd_custom_target(doxygen ALL\n  DEPENDS ${CMAKE_BINARY_DIR}/doxygen.log\n  )\n\ninstall(DIRECTORY\n  ${CMAKE_CURRENT_BINARY_DIR}/html\n  DESTINATION ${CMAKE_INSTALL_DOCDIR}/html\n  )\n"
  },
  {
    "path": "doc/headers/main.h",
    "content": "/**\n * @mainpage\n *\n * This is the main starting page for the technical ryujin class and\n * function documentation. A full usage guide can be found at\n * https://conservation-laws.org\n *\n * The program is organized into the following modules:\n *\n * @dot\n digraph G\n{\n  graph[rankdir=\"TB\",bgcolor=\"transparent\"];\n\n  node [fontname=\"FreeSans\",fontsize=15,\n        shape=record,height=0.2,width=0.4,\n        color=\"black\", fillcolor=\"white\", style=\"filled\"];\n  edge [color=\"black\", weight=10];\n\n  simd  [label=\"SIMD\", URL=\"\\ref SIMD\"];\n  fe    [label=\"Finite element formulation\", URL=\"\\ref FiniteElement\"];\n  misc  [label=\"Miscellaneous\", URL=\"\\ref Miscellaneous\"];\n  mesh  [label=\"Discretization\", URL=\"\\ref Mesh\"];\n  hyperbolic [label=\"Hyperbolic Module\", URL=\"\\ref HyperbolicModule\"];\n  parabolic [label=\"Parabolic Module\", URL=\"\\ref ParabolicModule\"];\n  loop  [label=\"Time Integration and Postprocessing\", URL=\"\\ref TimeLoop\"];\n\n  formulation [label=\"PDE formulation\", URL=\"\\ref Description\"];\n\n  euler [label=\"Euler Equations\", URL=\"\\ref EulerEquations\"];\n  shallow [label=\"Shallow Water Equations\", URL=\"\\ref ShallowWaterEquations\"];\n  navier [label=\"Navier Stokes Equations\", URL=\"\\ref NavierStokesEquations\"];\n\n  fe   -> mesh  [color=\"black\",style=\"solid\"];\n  simd -> hyperbolic [color=\"black\",style=\"solid\"];\n  fe   -> hyperbolic [color=\"black\",style=\"solid\"];\n  misc -> hyperbolic [color=\"black\",style=\"solid\"];\n  mesh -> hyperbolic [color=\"black\",style=\"solid\"];\n  simd -> parabolic [color=\"black\",style=\"solid\"];\n  fe   -> parabolic [color=\"black\",style=\"solid\"];\n  misc -> parabolic [color=\"black\",style=\"solid\"];\n  mesh -> parabolic [color=\"black\",style=\"solid\"];\n  misc -> loop  [color=\"black\",style=\"solid\"];\n  hyperbolic -> loop [color=\"black\",style=\"solid\"];\n  parabolic -> loop [color=\"black\",style=\"solid\"];\n  euler -> formulation [color=\"blak\",style=\"solid\"];\n  shallow -> formulation [color=\"black\",style=\"solid\"];\n  navier -> formulation [color=\"black\",style=\"solid\"];\n  formulation -> hyperbolic [color=\"black\",style=\"solid\"];\n  formulation -> parabolic [color=\"black\",style=\"solid\"];\n}\n * @enddot\n *\n * ryujin is based on discretization approaches and algorithms that have\n * been developed in a number of publications\n * \\cite GuermondPopov2016\n * \\cite GuermondPopov2016b\n * \\cite GuermondEtAl2018\n * \\cite GuermondEtAl2018SW\n * \\cite ryujin-2021-1\n * \\cite ryujin-2021-2\n * \\cite ryujin-2021-3\n * \\cite ClaytonGuermondPopov-2022\n * \\cite ryujin-2023-4.\n * A complete list of references can be found in the \\ref citelist.\n */\n"
  },
  {
    "path": "doc/headers/modules.h",
    "content": "/**\n * @defgroup Mesh Mesh generation and discretization\n *\n * Several classes and helper functions for creating meshes for a number of\n * benchmark configurations and controlling the finite element\n * discretization.\n */\n\n\n/**\n * @defgroup FiniteElement Finite element formulation\n *\n * Some helper functions for local index handling, dof renumbering,\n * sparsity pattern creation and matrix assembly.\n */\n\n\n/**\n * @defgroup Miscellaneous Miscellaneous\n *\n * Miscellaneous helper functions, macros and classes.\n */\n\n\n/**\n * @defgroup SIMD SIMD\n *\n * SIMD related functions and classes.\n */\n\n\n/**\n * @defgroup HyperbolicModule Hyperbolic Module\n *\n * This module contains classes and functions used during different stages\n * of the explicit hyperbolic update performed in HyperbolicModule::step().\n */\n\n\n/**\n * @defgroup ParabolicModule Parabolic Module\n *\n * This module contains classes and functions used during different stages\n * of the implicit parabolic update performed in ParabolicModule::step().\n */\n\n\n/**\n * @defgroup TimeLoop Time Integration and Postprocessing\n *\n * This module contains classes and functions used for timestepping and\n * running the program.\n */\n\n\n/**\n * @defgroup EulerEquations The Compressible Euler Equations\n *\n * This module contains classes and functions related to solving the\n * compressible Euler equations of gas dynamics.\n */\n\n\n/**\n * @defgroup NavierStokesEquations The Compressible Navier Stokes Equations\n *\n * This module contains classes and functions related to solving the\n * compressible Navier Stokes equations.\n */\n\n\n/**\n * @defgroup ScalarConservationEquations The Scalar Conservation Equations\n *\n * This module contains classes and functions related to solving various\n * scalar conservation equations.\n */\n\n\n/**\n * @defgroup ShallowWaterEquations The Shallow Water Equations\n *\n * This module contains classes and functions related to solving the\n * shallow water equations.\n */\n\n\n/**\n * @defgroup SkeletonEquations Minimal equation interface\n *\n * This module contains the minimal necessary interface for defining a\n * hyperbolic system.\n */\n"
  },
  {
    "path": "doc/options.dox.in",
    "content": "# Doxyfile 1.9.7\n\n# This file describes the settings to be used by the documentation system\n# doxygen (www.doxygen.org) for a project.\n#\n# All text after a double hash (##) is considered a comment and is placed in\n# front of the TAG it is preceding.\n#\n# All text after a single hash (#) is considered a comment and will be ignored.\n# The format is:\n# TAG = value [value, ...]\n# For lists, items can also be appended using:\n# TAG += value [value, ...]\n# Values that contain spaces should be placed between quotes (\\\" \\\").\n#\n# Note:\n#\n# Use doxygen to compare the used configuration file with the template\n# configuration file:\n# doxygen -x [configFile]\n# Use doxygen to compare the used configuration file with the template\n# configuration file without replacing the environment variables or CMake type\n# replacement variables:\n# doxygen -x_noenv [configFile]\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\n\n# This tag specifies the encoding used for all characters in the configuration\n# file that follow. The default is UTF-8 which is also the encoding used for all\n# text before the first occurrence of this tag. Doxygen uses libiconv (or the\n# iconv built into libc) for the transcoding. See\n# https://www.gnu.org/software/libiconv/ for the list of possible encodings.\n# The default value is: UTF-8.\n\nDOXYFILE_ENCODING      = UTF-8\n\n# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by\n# double-quotes, unless you are using Doxywizard) that should identify the\n# project for which the documentation is generated. This name is used in the\n# title of most generated pages and in a few other places.\n# The default value is: My Project.\n\nPROJECT_NAME           = ryujin\n\n# The PROJECT_NUMBER tag can be used to enter a project or revision number. This\n# could be handy for archiving the generated documentation or if some version\n# control system is used.\n\nPROJECT_NUMBER         = \"@RYUJIN_VERSION@ revision @GIT_REVISION@\"\n\n# Using the PROJECT_BRIEF tag one can provide an optional one line description\n# for a project that appears at the top of each page and should give viewer a\n# quick idea about the purpose of the project. Keep the description short.\n\nPROJECT_BRIEF          =\n\n# With the PROJECT_LOGO tag one can specify a logo or an icon that is included\n# in the documentation. The maximum height of the logo should not exceed 55\n# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy\n# the logo to the output directory.\n\nPROJECT_LOGO           = @CMAKE_CURRENT_SOURCE_DIR@/logo.png\n\n# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path\n# into which the generated documentation will be written. If a relative path is\n# entered, it will be relative to the location where doxygen was started. If\n# left blank the current directory will be used.\n\nOUTPUT_DIRECTORY       =\n\n# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096\n# sub-directories (in 2 levels) under the output directory of each output format\n# and will distribute the generated files over these directories. Enabling this\n# option can be useful when feeding doxygen a huge amount of source files, where\n# putting all generated files in the same directory would otherwise causes\n# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to\n# control the number of sub-directories.\n# The default value is: NO.\n\nCREATE_SUBDIRS         = NO\n\n# Controls the number of sub-directories that will be created when\n# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every\n# level increment doubles the number of directories, resulting in 4096\n# directories at level 8 which is the default and also the maximum value. The\n# sub-directories are organized in 2 levels, the first level always has a fixed\n# number of 16 directories.\n# Minimum value: 0, maximum value: 8, default value: 8.\n# This tag requires that the tag CREATE_SUBDIRS is set to YES.\n\nCREATE_SUBDIRS_LEVEL   = 8\n\n# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII\n# characters to appear in the names of generated files. If set to NO, non-ASCII\n# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode\n# U+3044.\n# The default value is: NO.\n\nALLOW_UNICODE_NAMES    = NO\n\n# The OUTPUT_LANGUAGE tag is used to specify the language in which all\n# documentation generated by doxygen is written. Doxygen will use this\n# information to generate all constant output in the proper language.\n# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian,\n# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English\n# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek,\n# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with\n# English messages), Korean, Korean-en (Korean with English messages), Latvian,\n# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese,\n# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish,\n# Swedish, Turkish, Ukrainian and Vietnamese.\n# The default value is: English.\n\nOUTPUT_LANGUAGE        = English\n\n# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member\n# descriptions after the members that are listed in the file and class\n# documentation (similar to Javadoc). Set to NO to disable this.\n# The default value is: YES.\n\nBRIEF_MEMBER_DESC      = YES\n\n# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief\n# description of a member or function before the detailed description\n#\n# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the\n# brief descriptions will be completely suppressed.\n# The default value is: YES.\n\nREPEAT_BRIEF           = YES\n\n# This tag implements a quasi-intelligent brief description abbreviator that is\n# used to form the text in various listings. Each string in this list, if found\n# as the leading text of the brief description, will be stripped from the text\n# and the result, after processing the whole list, is used as the annotated\n# text. Otherwise, the brief description is used as-is. If left blank, the\n# following values are used ($name is automatically replaced with the name of\n# the entity):The $name class, The $name widget, The $name file, is, provides,\n# specifies, contains, represents, a, an and the.\n\nABBREVIATE_BRIEF       = \"The $name class\" \\\n                         \"The $name widget\" \\\n                         \"The $name file\" \\\n                         is \\\n                         provides \\\n                         specifies \\\n                         contains \\\n                         represents \\\n                         a \\\n                         an \\\n                         the\n\n# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then\n# doxygen will generate a detailed section even if there is only a brief\n# description.\n# The default value is: NO.\n\nALWAYS_DETAILED_SEC    = NO\n\n# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all\n# inherited members of a class in the documentation of that class as if those\n# members were ordinary class members. Constructors, destructors and assignment\n# operators of the base classes will not be shown.\n# The default value is: NO.\n\nINLINE_INHERITED_MEMB  = NO\n\n# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path\n# before files name in the file list and in the header files. If set to NO the\n# shortest path that makes the file name unique will be used\n# The default value is: YES.\n\nFULL_PATH_NAMES        = YES\n\n# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.\n# Stripping is only done if one of the specified strings matches the left-hand\n# part of the path. The tag can be used to show relative paths in the file list.\n# If left blank the directory from which doxygen is run is used as the path to\n# strip.\n#\n# Note that you can specify absolute paths here, but also relative paths, which\n# will be relative from the directory where doxygen is started.\n# This tag requires that the tag FULL_PATH_NAMES is set to YES.\n\nSTRIP_FROM_PATH        = @CMAKE_SOURCE_DIR@ \\\n                         @CMAKE_BINARY_DIR@\n\n# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the\n# path mentioned in the documentation of a class, which tells the reader which\n# header file to include in order to use a class. If left blank only the name of\n# the header file containing the class definition is used. Otherwise one should\n# specify the list of include paths that are normally passed to the compiler\n# using the -I flag.\n\nSTRIP_FROM_INC_PATH    = @CMAKE_SOURCE_DIR@ \\\n                         @CMAKE_BINARY_DIR@\n\n# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but\n# less readable) file names. This can be useful is your file systems doesn't\n# support long names like on DOS, Mac, or CD-ROM.\n# The default value is: NO.\n\nSHORT_NAMES            = NO\n\n# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the\n# first line (until the first dot) of a Javadoc-style comment as the brief\n# description. If set to NO, the Javadoc-style will behave just like regular Qt-\n# style comments (thus requiring an explicit @brief command for a brief\n# description.)\n# The default value is: NO.\n\nJAVADOC_AUTOBRIEF      = NO\n\n# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line\n# such as\n# /***************\n# as being the beginning of a Javadoc-style comment \"banner\". If set to NO, the\n# Javadoc-style will behave just like regular comments and it will not be\n# interpreted by doxygen.\n# The default value is: NO.\n\nJAVADOC_BANNER         = NO\n\n# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first\n# line (until the first dot) of a Qt-style comment as the brief description. If\n# set to NO, the Qt-style will behave just like regular Qt-style comments (thus\n# requiring an explicit \\brief command for a brief description.)\n# The default value is: NO.\n\nQT_AUTOBRIEF           = NO\n\n# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a\n# multi-line C++ special comment block (i.e. a block of //! or /// comments) as\n# a brief description. This used to be the default behavior. The new default is\n# to treat a multi-line C++ comment block as a detailed description. Set this\n# tag to YES if you prefer the old behavior instead.\n#\n# Note that setting this tag to YES also means that rational rose comments are\n# not recognized any more.\n# The default value is: NO.\n\nMULTILINE_CPP_IS_BRIEF = NO\n\n# By default Python docstrings are displayed as preformatted text and doxygen's\n# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the\n# doxygen's special commands can be used and the contents of the docstring\n# documentation blocks is shown as doxygen documentation.\n# The default value is: YES.\n\nPYTHON_DOCSTRING       = YES\n\n# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the\n# documentation from any documented member that it re-implements.\n# The default value is: YES.\n\nINHERIT_DOCS           = YES\n\n# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new\n# page for each member. If set to NO, the documentation of a member will be part\n# of the file/class/namespace that contains it.\n# The default value is: NO.\n\nSEPARATE_MEMBER_PAGES  = NO\n\n# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen\n# uses this value to replace tabs by spaces in code fragments.\n# Minimum value: 1, maximum value: 16, default value: 4.\n\nTAB_SIZE               = 4\n\n# This tag can be used to specify a number of aliases that act as commands in\n# the documentation. An alias has the form:\n# name=value\n# For example adding\n# \"sideeffect=@par Side Effects:^^\"\n# will allow you to put the command \\sideeffect (or @sideeffect) in the\n# documentation, which will result in a user-defined paragraph with heading\n# \"Side Effects:\". Note that you cannot put \\n's in the value part of an alias\n# to insert newlines (in the resulting output). You can put ^^ in the value part\n# of an alias to insert a newline as if a physical newline was in the original\n# file. When you need a literal { or } or , in the value part of an alias you\n# have to escape them by means of a backslash (\\), this can lead to conflicts\n# with the commands \\{ and \\} for these it is advised to use the version @{ and\n# @} or use a double escape (\\\\{ and \\\\})\n\nALIASES                =\n\n# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources\n# only. Doxygen will then generate output that is more tailored for C. For\n# instance, some of the names that are used will be different. The list of all\n# members will be omitted, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_FOR_C  = NO\n\n# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or\n# Python sources only. Doxygen will then generate output that is more tailored\n# for that language. For instance, namespaces will be presented as packages,\n# qualified scopes will look different, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_JAVA   = NO\n\n# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran\n# sources. Doxygen will then generate output that is tailored for Fortran.\n# The default value is: NO.\n\nOPTIMIZE_FOR_FORTRAN   = NO\n\n# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL\n# sources. Doxygen will then generate output that is tailored for VHDL.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_VHDL   = NO\n\n# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice\n# sources only. Doxygen will then generate output that is more tailored for that\n# language. For instance, namespaces will be presented as modules, types will be\n# separated into more groups, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_SLICE  = NO\n\n# Doxygen selects the parser to use depending on the extension of the files it\n# parses. With this tag you can assign which parser to use for a given\n# extension. Doxygen has a built-in mapping, but you can override or extend it\n# using this tag. The format is ext=language, where ext is a file extension, and\n# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,\n# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,\n# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:\n# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser\n# tries to guess whether the code is fixed or free formatted code, this is the\n# default for Fortran type files). For instance to make doxygen treat .inc files\n# as Fortran files (default is PHP), and .f files as C (default is Fortran),\n# use: inc=Fortran f=C.\n#\n# Note: For files without extension you can use no_extension as a placeholder.\n#\n# Note that for custom extensions you also need to set FILE_PATTERNS otherwise\n# the files are not read by doxygen. When specifying no_extension you should add\n# * to the FILE_PATTERNS.\n#\n# Note see also the list of default file extension mappings.\n\nEXTENSION_MAPPING      =\n\n# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments\n# according to the Markdown format, which allows for more readable\n# documentation. See https://daringfireball.net/projects/markdown/ for details.\n# The output of markdown processing is further processed by doxygen, so you can\n# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in\n# case of backward compatibilities issues.\n# The default value is: YES.\n\nMARKDOWN_SUPPORT       = YES\n\n# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up\n# to that level are automatically included in the table of contents, even if\n# they do not have an id attribute.\n# Note: This feature currently applies only to Markdown headings.\n# Minimum value: 0, maximum value: 99, default value: 5.\n# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.\n\nTOC_INCLUDE_HEADINGS   = 5\n\n# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to\n# generate identifiers for the Markdown headings. Note: Every identifier is\n# unique.\n# Possible values are: DOXYGEN Use a fixed 'autotoc_md' string followed by a\n# sequence number starting at 0. and GITHUB Use the lower case version of title\n# with any whitespace replaced by '-' and punctations characters removed..\n# The default value is: DOXYGEN.\n# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.\n\nMARKDOWN_ID_STYLE      = DOXYGEN\n\n# When enabled doxygen tries to link words that correspond to documented\n# classes, or namespaces to their corresponding documentation. Such a link can\n# be prevented in individual cases by putting a % sign in front of the word or\n# globally by setting AUTOLINK_SUPPORT to NO.\n# The default value is: YES.\n\nAUTOLINK_SUPPORT       = YES\n\n# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want\n# to include (a tag file for) the STL sources as input, then you should set this\n# tag to YES in order to let doxygen match functions declarations and\n# definitions whose arguments contain STL classes (e.g. func(std::string);\n# versus func(std::string) {}). This also make the inheritance and collaboration\n# diagrams that involve STL classes more complete and accurate.\n# The default value is: NO.\n\nBUILTIN_STL_SUPPORT    = YES\n\n# If you use Microsoft's C++/CLI language, you should set this option to YES to\n# enable parsing support.\n# The default value is: NO.\n\nCPP_CLI_SUPPORT        = NO\n\n# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:\n# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen\n# will parse them like normal C++ but will assume all classes use public instead\n# of private inheritance when no explicit protection keyword is present.\n# The default value is: NO.\n\nSIP_SUPPORT            = NO\n\n# For Microsoft's IDL there are propget and propput attributes to indicate\n# getter and setter methods for a property. Setting this option to YES will make\n# doxygen to replace the get and set methods by a property in the documentation.\n# This will only work if the methods are indeed getting or setting a simple\n# type. If this is not the case, or you want to show the methods anyway, you\n# should set this option to NO.\n# The default value is: YES.\n\nIDL_PROPERTY_SUPPORT   = YES\n\n# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC\n# tag is set to YES then doxygen will reuse the documentation of the first\n# member in the group (if any) for the other members of the group. By default\n# all members of a group must be documented explicitly.\n# The default value is: NO.\n\nDISTRIBUTE_GROUP_DOC   = NO\n\n# If one adds a struct or class to a group and this option is enabled, then also\n# any nested class or struct is added to the same group. By default this option\n# is disabled and one has to add nested compounds explicitly via \\ingroup.\n# The default value is: NO.\n\nGROUP_NESTED_COMPOUNDS = NO\n\n# Set the SUBGROUPING tag to YES to allow class member groups of the same type\n# (for instance a group of public functions) to be put as a subgroup of that\n# type (e.g. under the Public Functions section). Set it to NO to prevent\n# subgrouping. Alternatively, this can be done per class using the\n# \\nosubgrouping command.\n# The default value is: YES.\n\nSUBGROUPING            = YES\n\n# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions\n# are shown inside the group in which they are included (e.g. using \\ingroup)\n# instead of on a separate page (for HTML and Man pages) or section (for LaTeX\n# and RTF).\n#\n# Note that this feature does not work in combination with\n# SEPARATE_MEMBER_PAGES.\n# The default value is: NO.\n\nINLINE_GROUPED_CLASSES = NO\n\n# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions\n# with only public data fields or simple typedef fields will be shown inline in\n# the documentation of the scope in which they are defined (i.e. file,\n# namespace, or group documentation), provided this scope is documented. If set\n# to NO, structs, classes, and unions are shown on a separate page (for HTML and\n# Man pages) or section (for LaTeX and RTF).\n# The default value is: NO.\n\nINLINE_SIMPLE_STRUCTS  = NO\n\n# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or\n# enum is documented as struct, union, or enum with the name of the typedef. So\n# typedef struct TypeS {} TypeT, will appear in the documentation as a struct\n# with name TypeT. When disabled the typedef will appear as a member of a file,\n# namespace, or class. And the struct will be named TypeS. This can typically be\n# useful for C code in case the coding convention dictates that all compound\n# types are typedef'ed and only the typedef is referenced, never the tag name.\n# The default value is: NO.\n\nTYPEDEF_HIDES_STRUCT   = NO\n\n# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This\n# cache is used to resolve symbols given their name and scope. Since this can be\n# an expensive process and often the same symbol appears multiple times in the\n# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small\n# doxygen will become slower. If the cache is too large, memory is wasted. The\n# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range\n# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536\n# symbols. At the end of a run doxygen will report the cache usage and suggest\n# the optimal cache size from a speed point of view.\n# Minimum value: 0, maximum value: 9, default value: 0.\n\nLOOKUP_CACHE_SIZE      = 0\n\n# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use\n# during processing. When set to 0 doxygen will based this on the number of\n# cores available in the system. You can set it explicitly to a value larger\n# than 0 to get more control over the balance between CPU load and processing\n# speed. At this moment only the input processing can be done using multiple\n# threads. Since this is still an experimental feature the default is set to 1,\n# which effectively disables parallel processing. Please report any issues you\n# encounter. Generating dot graphs in parallel is controlled by the\n# DOT_NUM_THREADS setting.\n# Minimum value: 0, maximum value: 32, default value: 1.\n\nNUM_PROC_THREADS       = 1\n\n# If the TIMESTAMP tag is set different from NO then each generated page will\n# contain the date or date and time when the page was generated. Setting this to\n# NO can help when comparing the output of multiple runs.\n# Possible values are: YES, NO, DATETIME and DATE.\n# The default value is: NO.\n\nTIMESTAMP              = NO\n\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\n\n# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in\n# documentation are documented, even if no documentation was available. Private\n# class members and static file members will be hidden unless the\n# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.\n# Note: This will also disable the warnings about undocumented members that are\n# normally produced when WARNINGS is set to YES.\n# The default value is: NO.\n\nEXTRACT_ALL            = YES\n\n# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will\n# be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIVATE        = NO\n\n# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual\n# methods of a class will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIV_VIRTUAL   = NO\n\n# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal\n# scope will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PACKAGE        = NO\n\n# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be\n# included in the documentation.\n# The default value is: NO.\n\nEXTRACT_STATIC         = YES\n\n# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined\n# locally in source files will be included in the documentation. If set to NO,\n# only classes defined in header files are included. Does not have any effect\n# for Java sources.\n# The default value is: YES.\n\nEXTRACT_LOCAL_CLASSES  = YES\n\n# This flag is only useful for Objective-C code. If set to YES, local methods,\n# which are defined in the implementation section but not in the interface are\n# included in the documentation. If set to NO, only methods in the interface are\n# included.\n# The default value is: NO.\n\nEXTRACT_LOCAL_METHODS  = NO\n\n# If this flag is set to YES, the members of anonymous namespaces will be\n# extracted and appear in the documentation as a namespace called\n# 'anonymous_namespace{file}', where file will be replaced with the base name of\n# the file that contains the anonymous namespace. By default anonymous namespace\n# are hidden.\n# The default value is: NO.\n\nEXTRACT_ANON_NSPACES   = NO\n\n# If this flag is set to YES, the name of an unnamed parameter in a declaration\n# will be determined by the corresponding definition. By default unnamed\n# parameters remain unnamed in the output.\n# The default value is: YES.\n\nRESOLVE_UNNAMED_PARAMS = YES\n\n# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all\n# undocumented members inside documented classes or files. If set to NO these\n# members will be included in the various overviews, but no documentation\n# section is generated. This option has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_MEMBERS     = NO\n\n# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all\n# undocumented classes that are normally visible in the class hierarchy. If set\n# to NO, these classes will be included in the various overviews. This option\n# will also hide undocumented C++ concepts if enabled. This option has no effect\n# if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_CLASSES     = NO\n\n# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend\n# declarations. If set to NO, these declarations will be included in the\n# documentation.\n# The default value is: NO.\n\nHIDE_FRIEND_COMPOUNDS  = NO\n\n# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any\n# documentation blocks found inside the body of a function. If set to NO, these\n# blocks will be appended to the function's detailed documentation block.\n# The default value is: NO.\n\nHIDE_IN_BODY_DOCS      = NO\n\n# The INTERNAL_DOCS tag determines if documentation that is typed after a\n# \\internal command is included. If the tag is set to NO then the documentation\n# will be excluded. Set it to YES to include the internal documentation.\n# The default value is: NO.\n\nINTERNAL_DOCS          = NO\n\n# With the correct setting of option CASE_SENSE_NAMES doxygen will better be\n# able to match the capabilities of the underlying filesystem. In case the\n# filesystem is case sensitive (i.e. it supports files in the same directory\n# whose names only differ in casing), the option must be set to YES to properly\n# deal with such files in case they appear in the input. For filesystems that\n# are not case sensitive the option should be set to NO to properly deal with\n# output files written for symbols that only differ in casing, such as for two\n# classes, one named CLASS and the other named Class, and to also support\n# references to files without having to specify the exact matching casing. On\n# Windows (including Cygwin) and MacOS, users should typically set this option\n# to NO, whereas on Linux or other Unix flavors it should typically be set to\n# YES.\n# Possible values are: SYSTEM, NO and YES.\n# The default value is: SYSTEM.\n\nCASE_SENSE_NAMES       = YES\n\n# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with\n# their full class and namespace scopes in the documentation. If set to YES, the\n# scope will be hidden.\n# The default value is: NO.\n\nHIDE_SCOPE_NAMES       = NO\n\n# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will\n# append additional text to a page's title, such as Class Reference. If set to\n# YES the compound reference will be hidden.\n# The default value is: NO.\n\nHIDE_COMPOUND_REFERENCE= NO\n\n# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class\n# will show which file needs to be included to use the class.\n# The default value is: YES.\n\nSHOW_HEADERFILE        = YES\n\n# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of\n# the files that are included by a file in the documentation of that file.\n# The default value is: YES.\n\nSHOW_INCLUDE_FILES     = YES\n\n# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each\n# grouped member an include statement to the documentation, telling the reader\n# which file to include in order to use the member.\n# The default value is: NO.\n\nSHOW_GROUPED_MEMB_INC  = NO\n\n# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include\n# files with double quotes in the documentation rather than with sharp brackets.\n# The default value is: NO.\n\nFORCE_LOCAL_INCLUDES   = NO\n\n# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the\n# documentation for inline members.\n# The default value is: YES.\n\nINLINE_INFO            = YES\n\n# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the\n# (detailed) documentation of file and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order.\n# The default value is: YES.\n\nSORT_MEMBER_DOCS       = NO\n\n# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief\n# descriptions of file, namespace and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order. Note that\n# this will also influence the order of the classes in the class list.\n# The default value is: NO.\n\nSORT_BRIEF_DOCS        = NO\n\n# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the\n# (brief and detailed) documentation of class members so that constructors and\n# destructors are listed first. If set to NO the constructors will appear in the\n# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.\n# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief\n# member documentation.\n# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting\n# detailed member documentation.\n# The default value is: NO.\n\nSORT_MEMBERS_CTORS_1ST = NO\n\n# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy\n# of group names into alphabetical order. If set to NO the group names will\n# appear in their defined order.\n# The default value is: NO.\n\nSORT_GROUP_NAMES       = NO\n\n# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by\n# fully-qualified names, including namespaces. If set to NO, the class list will\n# be sorted only by class name, not including the namespace part.\n# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.\n# Note: This option applies only to the class list, not to the alphabetical\n# list.\n# The default value is: NO.\n\nSORT_BY_SCOPE_NAME     = NO\n\n# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper\n# type resolution of all parameters of a function it will reject a match between\n# the prototype and the implementation of a member function even if there is\n# only one candidate or it is obvious which candidate to choose by doing a\n# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still\n# accept a match between prototype and implementation in such cases.\n# The default value is: NO.\n\nSTRICT_PROTO_MATCHING  = NO\n\n# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo\n# list. This list is created by putting \\todo commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TODOLIST      = YES\n\n# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test\n# list. This list is created by putting \\test commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TESTLIST      = YES\n\n# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug\n# list. This list is created by putting \\bug commands in the documentation.\n# The default value is: YES.\n\nGENERATE_BUGLIST       = YES\n\n# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)\n# the deprecated list. This list is created by putting \\deprecated commands in\n# the documentation.\n# The default value is: YES.\n\nGENERATE_DEPRECATEDLIST= YES\n\n# The ENABLED_SECTIONS tag can be used to enable conditional documentation\n# sections, marked by \\if <section_label> ... \\endif and \\cond <section_label>\n# ... \\endcond blocks.\n\nENABLED_SECTIONS       =\n\n# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the\n# initial value of a variable or macro / define can have for it to appear in the\n# documentation. If the initializer consists of more lines than specified here\n# it will be hidden. Use a value of 0 to hide initializers completely. The\n# appearance of the value of individual variables and macros / defines can be\n# controlled using \\showinitializer or \\hideinitializer command in the\n# documentation regardless of this setting.\n# Minimum value: 0, maximum value: 10000, default value: 30.\n\nMAX_INITIALIZER_LINES  = 30\n\n# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at\n# the bottom of the documentation of classes and structs. If set to YES, the\n# list will mention the files that were used to generate the documentation.\n# The default value is: YES.\n\nSHOW_USED_FILES        = YES\n\n# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This\n# will remove the Files entry from the Quick Index and from the Folder Tree View\n# (if specified).\n# The default value is: YES.\n\nSHOW_FILES             = YES\n\n# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces\n# page. This will remove the Namespaces entry from the Quick Index and from the\n# Folder Tree View (if specified).\n# The default value is: YES.\n\nSHOW_NAMESPACES        = YES\n\n# The FILE_VERSION_FILTER tag can be used to specify a program or script that\n# doxygen should invoke to get the current version for each file (typically from\n# the version control system). Doxygen will invoke the program by executing (via\n# popen()) the command command input-file, where command is the value of the\n# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided\n# by doxygen. Whatever the program writes to standard output is used as the file\n# version. For an example see the documentation.\n\nFILE_VERSION_FILTER    =\n\n# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed\n# by doxygen. The layout file controls the global structure of the generated\n# output files in an output format independent way. To create the layout file\n# that represents doxygen's defaults, run doxygen with the -l option. You can\n# optionally specify a file name after the option, if omitted DoxygenLayout.xml\n# will be used as the name of the layout file. See also section \"Changing the\n# layout of pages\" for information.\n#\n# Note that if you run doxygen from a directory containing a file called\n# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE\n# tag is left empty.\n\nLAYOUT_FILE            =\n\n# The CITE_BIB_FILES tag can be used to specify one or more bib files containing\n# the reference definitions. This must be a list of .bib files. The .bib\n# extension is automatically appended if omitted. This requires the bibtex tool\n# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.\n# For LaTeX the style of the bibliography can be controlled using\n# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the\n# search path. See also \\cite for info how to create references.\n\nCITE_BIB_FILES         = @CMAKE_CURRENT_SOURCE_DIR@/references.bib\n\n#---------------------------------------------------------------------------\n# Configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\n\n# The QUIET tag can be used to turn on/off the messages that are generated to\n# standard output by doxygen. If QUIET is set to YES this implies that the\n# messages are off.\n# The default value is: NO.\n\nQUIET                  = YES\n\n# The WARNINGS tag can be used to turn on/off the warning messages that are\n# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES\n# this implies that the warnings are on.\n#\n# Tip: Turn warnings on while writing the documentation.\n# The default value is: YES.\n\nWARNINGS               = YES\n\n# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate\n# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag\n# will automatically be disabled.\n# The default value is: YES.\n\nWARN_IF_UNDOCUMENTED   = YES\n\n# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for\n# potential errors in the documentation, such as documenting some parameters in\n# a documented function twice, or documenting parameters that don't exist or\n# using markup commands wrongly.\n# The default value is: YES.\n\nWARN_IF_DOC_ERROR      = YES\n\n# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete\n# function parameter documentation. If set to NO, doxygen will accept that some\n# parameters have no documentation without warning.\n# The default value is: YES.\n\nWARN_IF_INCOMPLETE_DOC = YES\n\n# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that\n# are documented, but have no documentation for their parameters or return\n# value. If set to NO, doxygen will only warn about wrong parameter\n# documentation, but not about the absence of documentation. If EXTRACT_ALL is\n# set to YES then this flag will automatically be disabled. See also\n# WARN_IF_INCOMPLETE_DOC\n# The default value is: NO.\n\nWARN_NO_PARAMDOC       = NO\n\n# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about\n# undocumented enumeration values. If set to NO, doxygen will accept\n# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag\n# will automatically be disabled.\n# The default value is: NO.\n\nWARN_IF_UNDOC_ENUM_VAL = NO\n\n# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when\n# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS\n# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but\n# at the end of the doxygen process doxygen will return with a non-zero status.\n# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves\n# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not\n# write the warning messages in between other messages but write them at the end\n# of a run, in case a WARN_LOGFILE is defined the warning messages will be\n# besides being in the defined file also be shown at the end of a run, unless\n# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case\n# the behavior will remain as with the setting FAIL_ON_WARNINGS.\n# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT.\n# The default value is: NO.\n\nWARN_AS_ERROR          = NO\n\n# The WARN_FORMAT tag determines the format of the warning messages that doxygen\n# can produce. The string should contain the $file, $line, and $text tags, which\n# will be replaced by the file and line number from which the warning originated\n# and the warning text. Optionally the format may contain $version, which will\n# be replaced by the version of the file (if it could be obtained via\n# FILE_VERSION_FILTER)\n# See also: WARN_LINE_FORMAT\n# The default value is: $file:$line: $text.\n\nWARN_FORMAT            = \"$file:$line: $text\"\n\n# In the $text part of the WARN_FORMAT command it is possible that a reference\n# to a more specific place is given. To make it easier to jump to this place\n# (outside of doxygen) the user can define a custom \"cut\" / \"paste\" string.\n# Example:\n# WARN_LINE_FORMAT = \"'vi $file +$line'\"\n# See also: WARN_FORMAT\n# The default value is: at line $line of file $file.\n\nWARN_LINE_FORMAT       = \"at line $line of file $file\"\n\n# The WARN_LOGFILE tag can be used to specify a file to which warning and error\n# messages should be written. If left blank the output is written to standard\n# error (stderr). In case the file specified cannot be opened for writing the\n# warning and error messages are written to standard error. When as file - is\n# specified the warning and error messages are written to standard output\n# (stdout).\n\nWARN_LOGFILE           =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the input files\n#---------------------------------------------------------------------------\n\n# The INPUT tag is used to specify the files and/or directories that contain\n# documented source files. You may enter file names like myfile.cpp or\n# directories like /usr/src/myproject. Separate the files or directories with\n# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING\n# Note: If this tag is empty the current directory is searched.\n\nINPUT                  = @DOXYGEN_INPUT@\n\n# This tag can be used to specify the character encoding of the source files\n# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses\n# libiconv (or the iconv built into libc) for the transcoding. See the libiconv\n# documentation (see:\n# https://www.gnu.org/software/libiconv/) for the list of possible encodings.\n# See also: INPUT_FILE_ENCODING\n# The default value is: UTF-8.\n\nINPUT_ENCODING         = UTF-8\n\n# This tag can be used to specify the character encoding of the source files\n# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify\n# character encoding on a per file pattern basis. Doxygen will compare the file\n# name with each pattern and apply the encoding instead of the default\n# INPUT_ENCODING) if there is a match. The character encodings are a list of the\n# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding\n# \"INPUT_ENCODING\" for further information on supported encodings.\n\nINPUT_FILE_ENCODING    =\n\n# If the value of the INPUT tag contains directories, you can use the\n# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and\n# *.h) to filter out the source-files in the directories.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# read by doxygen.\n#\n# Note the list of default checked file patterns might differ from the list of\n# default file extension mappings.\n#\n# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,\n# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,\n# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,\n# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C\n# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,\n# *.vhdl, *.ucf, *.qsf and *.ice.\n\nFILE_PATTERNS          =\n\n# The RECURSIVE tag can be used to specify whether or not subdirectories should\n# be searched for input files as well.\n# The default value is: NO.\n\nRECURSIVE              = NO\n\n# The EXCLUDE tag can be used to specify files and/or directories that should be\n# excluded from the INPUT source files. This way you can easily exclude a\n# subdirectory from a directory tree whose root is specified with the INPUT tag.\n#\n# Note that relative paths are relative to the directory from which doxygen is\n# run.\n\nEXCLUDE                =\n\n# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or\n# directories that are symbolic links (a Unix file system feature) are excluded\n# from the input.\n# The default value is: NO.\n\nEXCLUDE_SYMLINKS       = NO\n\n# If the value of the INPUT tag contains directories, you can use the\n# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude\n# certain files from those directories.\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories for example use the pattern */test/*\n\nEXCLUDE_PATTERNS       =\n\n# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names\n# (namespaces, classes, functions, etc.) that should be excluded from the\n# output. The symbol name can be a fully qualified name, a word, or if the\n# wildcard * is used, a substring. Examples: ANamespace, AClass,\n# ANamespace::AClass, ANamespace::*Test\n\nEXCLUDE_SYMBOLS        =\n\n# The EXAMPLE_PATH tag can be used to specify one or more files or directories\n# that contain example code fragments that are included (see the \\include\n# command).\n\nEXAMPLE_PATH           =\n\n# If the value of the EXAMPLE_PATH tag contains directories, you can use the\n# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and\n# *.h) to filter out the source-files in the directories. If left blank all\n# files are included.\n\nEXAMPLE_PATTERNS       =\n\n# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be\n# searched for input files to be used with the \\include or \\dontinclude commands\n# irrespective of the value of the RECURSIVE tag.\n# The default value is: NO.\n\nEXAMPLE_RECURSIVE      = NO\n\n# The IMAGE_PATH tag can be used to specify one or more files or directories\n# that contain images that are to be included in the documentation (see the\n# \\image command).\n\nIMAGE_PATH             =\n\n# The INPUT_FILTER tag can be used to specify a program that doxygen should\n# invoke to filter for each input file. Doxygen will invoke the filter program\n# by executing (via popen()) the command:\n#\n# <filter> <input-file>\n#\n# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the\n# name of an input file. Doxygen will then use the output that the filter\n# program writes to standard output. If FILTER_PATTERNS is specified, this tag\n# will be ignored.\n#\n# Note that the filter must not add or remove lines; it is applied before the\n# code is scanned, but not when the output code is generated. If lines are added\n# or removed, the anchors will not be placed correctly.\n#\n# Note that doxygen will use the data processed and written to standard output\n# for further processing, therefore nothing else, like debug statements or used\n# commands (so in case of a Windows batch file always use @echo OFF), should be\n# written to standard output.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by doxygen.\n\nINPUT_FILTER           =\n\n# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern\n# basis. Doxygen will compare the file name with each pattern and apply the\n# filter if there is a match. The filters are a list of the form: pattern=filter\n# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how\n# filters are used. If the FILTER_PATTERNS tag is empty or if none of the\n# patterns match the file name, INPUT_FILTER is applied.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by doxygen.\n\nFILTER_PATTERNS        =\n\n# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using\n# INPUT_FILTER) will also be used to filter the input files that are used for\n# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).\n# The default value is: NO.\n\nFILTER_SOURCE_FILES    = YES\n\n# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file\n# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and\n# it is also possible to disable source filtering for a specific pattern using\n# *.ext= (so without naming a filter).\n# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.\n\nFILTER_SOURCE_PATTERNS =\n\n# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that\n# is part of the input, its contents will be placed on the main page\n# (index.html). This can be useful if you have a project on for instance GitHub\n# and want to reuse the introduction page also for the doxygen output.\n\nUSE_MDFILE_AS_MAINPAGE =\n\n# The Fortran standard specifies that for fixed formatted Fortran code all\n# characters from position 72 are to be considered as comment. A common\n# extension is to allow longer lines before the automatic comment starts. The\n# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can\n# be processed before the automatic comment starts.\n# Minimum value: 7, maximum value: 10000, default value: 72.\n\nFORTRAN_COMMENT_AFTER  = 72\n\n#---------------------------------------------------------------------------\n# Configuration options related to source browsing\n#---------------------------------------------------------------------------\n\n# If the SOURCE_BROWSER tag is set to YES then a list of source files will be\n# generated. Documented entities will be cross-referenced with these sources.\n#\n# Note: To get rid of all source code in the generated output, make sure that\n# also VERBATIM_HEADERS is set to NO.\n# The default value is: NO.\n\nSOURCE_BROWSER         = YES\n\n# Setting the INLINE_SOURCES tag to YES will include the body of functions,\n# classes and enums directly into the documentation.\n# The default value is: NO.\n\nINLINE_SOURCES         = NO\n\n# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any\n# special comment blocks from generated source code fragments. Normal C, C++ and\n# Fortran comments will always remain visible.\n# The default value is: YES.\n\nSTRIP_CODE_COMMENTS    = YES\n\n# If the REFERENCED_BY_RELATION tag is set to YES then for each documented\n# entity all documented functions referencing it will be listed.\n# The default value is: NO.\n\nREFERENCED_BY_RELATION = YES\n\n# If the REFERENCES_RELATION tag is set to YES then for each documented function\n# all documented entities called/used by that function will be listed.\n# The default value is: NO.\n\nREFERENCES_RELATION    = YES\n\n# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set\n# to YES then the hyperlinks from functions in REFERENCES_RELATION and\n# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will\n# link to the documentation.\n# The default value is: YES.\n\nREFERENCES_LINK_SOURCE = YES\n\n# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the\n# source code will show a tooltip with additional information such as prototype,\n# brief description and links to the definition and documentation. Since this\n# will make the HTML file larger and loading of large files a bit slower, you\n# can opt to disable this feature.\n# The default value is: YES.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nSOURCE_TOOLTIPS        = YES\n\n# If the USE_HTAGS tag is set to YES then the references to source code will\n# point to the HTML generated by the htags(1) tool instead of doxygen built-in\n# source browser. The htags tool is part of GNU's global source tagging system\n# (see https://www.gnu.org/software/global/global.html). You will need version\n# 4.8.6 or higher.\n#\n# To use it do the following:\n# - Install the latest version of global\n# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file\n# - Make sure the INPUT points to the root of the source tree\n# - Run doxygen as normal\n#\n# Doxygen will invoke htags (and that will in turn invoke gtags), so these\n# tools must be available from the command line (i.e. in the search path).\n#\n# The result: instead of the source browser generated by doxygen, the links to\n# source code will now point to the output of htags.\n# The default value is: NO.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nUSE_HTAGS              = NO\n\n# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a\n# verbatim copy of the header file for each class for which an include is\n# specified. Set to NO to disable this.\n# See also: Section \\class.\n# The default value is: YES.\n\nVERBATIM_HEADERS       = YES\n\n# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the\n# clang parser (see:\n# http://clang.llvm.org/) for more accurate parsing at the cost of reduced\n# performance. This can be particularly helpful with template rich C++ code for\n# which doxygen's built-in parser lacks the necessary type information.\n# Note: The availability of this option depends on whether or not doxygen was\n# generated with the -Duse_libclang=ON option for CMake.\n# The default value is: NO.\n\nCLANG_ASSISTED_PARSING = NO\n\n# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS\n# tag is set to YES then doxygen will add the directory of each input to the\n# include path.\n# The default value is: YES.\n# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.\n\nCLANG_ADD_INC_PATHS    = YES\n\n# If clang assisted parsing is enabled you can provide the compiler with command\n# line options that you would normally use when invoking the compiler. Note that\n# the include paths will already be set by doxygen for the files and directories\n# specified with INPUT and INCLUDE_PATH.\n# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.\n\nCLANG_OPTIONS          =\n\n# If clang assisted parsing is enabled you can provide the clang parser with the\n# path to the directory containing a file called compile_commands.json. This\n# file is the compilation database (see:\n# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the\n# options used when the source files were built. This is equivalent to\n# specifying the -p option to a clang tool, such as clang-check. These options\n# will then be passed to the parser. Any options specified with CLANG_OPTIONS\n# will be added as well.\n# Note: The availability of this option depends on whether or not doxygen was\n# generated with the -Duse_libclang=ON option for CMake.\n\nCLANG_DATABASE_PATH    =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\n\n# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all\n# compounds will be generated. Enable this if the project contains a lot of\n# classes, structs, unions or interfaces.\n# The default value is: YES.\n\nALPHABETICAL_INDEX     = YES\n\n# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)\n# that should be ignored while generating the index headers. The IGNORE_PREFIX\n# tag works for classes, function and member names. The entity will be placed in\n# the alphabetical list under the first letter of the entity name that remains\n# after removing the prefix.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nIGNORE_PREFIX          =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the HTML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output\n# The default value is: YES.\n\nGENERATE_HTML          = YES\n\n# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_OUTPUT            = html\n\n# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each\n# generated HTML page (for example: .htm, .php, .asp).\n# The default value is: .html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FILE_EXTENSION    = .html\n\n# The HTML_HEADER tag can be used to specify a user-defined HTML header file for\n# each generated HTML page. If the tag is left blank doxygen will generate a\n# standard header.\n#\n# To get valid HTML the header file that includes any scripts and style sheets\n# that doxygen needs, which is dependent on the configuration options used (e.g.\n# the setting GENERATE_TREEVIEW). It is highly recommended to start with a\n# default header using\n# doxygen -w html new_header.html new_footer.html new_stylesheet.css\n# YourConfigFile\n# and then modify the file new_header.html. See also section \"Doxygen usage\"\n# for information on how to generate the default header that doxygen normally\n# uses.\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of doxygen. For a description\n# of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_HEADER            =\n\n# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each\n# generated HTML page. If the tag is left blank doxygen will generate a standard\n# footer. See HTML_HEADER for more information on how to generate a default\n# footer and what special commands can be used inside the footer. See also\n# section \"Doxygen usage\" for information on how to generate the default footer\n# that doxygen normally uses.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FOOTER            =\n\n# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style\n# sheet that is used by each HTML page. It can be used to fine-tune the look of\n# the HTML output. If left blank doxygen will generate a default style sheet.\n# See also section \"Doxygen usage\" for information on how to generate the style\n# sheet that doxygen normally uses.\n# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as\n# it is more robust and this tag (HTML_STYLESHEET) will in the future become\n# obsolete.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_STYLESHEET        =\n\n# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# cascading style sheets that are included after the standard style sheets\n# created by doxygen. Using this option one can overrule certain style aspects.\n# This is preferred over using HTML_STYLESHEET since it does not replace the\n# standard style sheet and is therefore more robust against future updates.\n# Doxygen will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list).\n# Note: Since the styling of scrollbars can currently not be overruled in\n# Webkit/Chromium, the styling will be left out of the default doxygen.css if\n# one or more extra stylesheets have been specified. So if scrollbar\n# customization is desired it has to be added explicitly. For an example see the\n# documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_STYLESHEET  =\n\n# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the HTML output directory. Note\n# that these files will be copied to the base HTML output directory. Use the\n# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these\n# files. In the HTML_STYLESHEET file, use the file name only. Also note that the\n# files will be copied as-is; there are no commands or markers available.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_FILES       =\n\n# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output\n# should be rendered with a dark or light theme.\n# Possible values are: LIGHT always generate light mode output, DARK always\n# generate dark mode output, AUTO_LIGHT automatically set the mode according to\n# the user preference, use light mode if no preference is set (the default),\n# AUTO_DARK automatically set the mode according to the user preference, use\n# dark mode if no preference is set and TOGGLE allow to user to switch between\n# light and dark mode via a button.\n# The default value is: AUTO_LIGHT.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE        = AUTO_LIGHT\n\n# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen\n# will adjust the colors in the style sheet and background images according to\n# this color. Hue is specified as an angle on a color-wheel, see\n# https://en.wikipedia.org/wiki/Hue for more information. For instance the value\n# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300\n# purple, and 360 is red again.\n# Minimum value: 0, maximum value: 359, default value: 220.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_HUE    = 220\n\n# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors\n# in the HTML output. For a value of 0 the output will use gray-scales only. A\n# value of 255 will produce the most vivid colors.\n# Minimum value: 0, maximum value: 255, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_SAT    = 100\n\n# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the\n# luminance component of the colors in the HTML output. Values below 100\n# gradually make the output lighter, whereas values above 100 make the output\n# darker. The value divided by 100 is the actual gamma applied, so 80 represents\n# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not\n# change the gamma.\n# Minimum value: 40, maximum value: 240, default value: 80.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_GAMMA  = 80\n\n# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML\n# documentation will contain a main index with vertical navigation menus that\n# are dynamically created via JavaScript. If disabled, the navigation index will\n# consists of multiple levels of tabs that are statically embedded in every HTML\n# page. Disable this option to support browsers that do not have JavaScript,\n# like the Qt help browser.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_MENUS     = YES\n\n# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML\n# documentation will contain sections that can be hidden and shown after the\n# page has loaded.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_SECTIONS  = NO\n\n# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries\n# shown in the various tree structured indices initially; the user can expand\n# and collapse entries dynamically later on. Doxygen will expand the tree to\n# such a level that at most the specified number of entries are visible (unless\n# a fully collapsed tree already exceeds this amount). So setting the number of\n# entries 1 will produce a full collapsed tree by default. 0 is a special value\n# representing an infinite number of entries and will result in a full expanded\n# tree by default.\n# Minimum value: 0, maximum value: 9999, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_INDEX_NUM_ENTRIES = 100\n\n# If the GENERATE_DOCSET tag is set to YES, additional index files will be\n# generated that can be used as input for Apple's Xcode 3 integrated development\n# environment (see:\n# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To\n# create a documentation set, doxygen will generate a Makefile in the HTML\n# output directory. Running make will produce the docset in that directory and\n# running make install will install the docset in\n# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at\n# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy\n# genXcode/_index.html for more information.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_DOCSET        = NO\n\n# This tag determines the name of the docset feed. A documentation feed provides\n# an umbrella under which multiple documentation sets from a single provider\n# (such as a company or product suite) can be grouped.\n# The default value is: Doxygen generated docs.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_FEEDNAME        = \"Doxygen generated docs\"\n\n# This tag determines the URL of the docset feed. A documentation feed provides\n# an umbrella under which multiple documentation sets from a single provider\n# (such as a company or product suite) can be grouped.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_FEEDURL         =\n\n# This tag specifies a string that should uniquely identify the documentation\n# set bundle. This should be a reverse domain-name style string, e.g.\n# com.mycompany.MyDocSet. Doxygen will append .docset to the name.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_BUNDLE_ID       = org.doxygen.Project\n\n# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify\n# the documentation publisher. This should be a reverse domain-name style\n# string, e.g. com.mycompany.MyDocSet.documentation.\n# The default value is: org.doxygen.Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_ID    = org.doxygen.Publisher\n\n# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.\n# The default value is: Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_NAME  = Publisher\n\n# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three\n# additional HTML index files: index.hhp, index.hhc, and index.hhk. The\n# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop\n# on Windows. In the beginning of 2021 Microsoft took the original page, with\n# a.o. the download links, offline the HTML help workshop was already many years\n# in maintenance mode). You can download the HTML help workshop from the web\n# archives at Installation executable (see:\n# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo\n# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).\n#\n# The HTML Help Workshop contains a compiler that can convert all HTML output\n# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML\n# files are now used as the Windows 98 help format, and will replace the old\n# Windows help format (.hlp) on all Windows platforms in the future. Compressed\n# HTML files also contain an index, a table of contents, and you can search for\n# words in the documentation. The HTML workshop also contains a viewer for\n# compressed HTML files.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_HTMLHELP      = NO\n\n# The CHM_FILE tag can be used to specify the file name of the resulting .chm\n# file. You can add a path in front of the file if the result should not be\n# written to the html output directory.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_FILE               =\n\n# The HHC_LOCATION tag can be used to specify the location (absolute path\n# including file name) of the HTML help compiler (hhc.exe). If non-empty,\n# doxygen will try to run the HTML help compiler on the generated index.hhp.\n# The file has to be specified with full path.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nHHC_LOCATION           =\n\n# The GENERATE_CHI flag controls if a separate .chi index file is generated\n# (YES) or that it should be included in the main .chm file (NO).\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nGENERATE_CHI           = NO\n\n# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)\n# and project file content.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_INDEX_ENCODING     =\n\n# The BINARY_TOC flag controls whether a binary table of contents is generated\n# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it\n# enables the Previous and Next buttons.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nBINARY_TOC             = NO\n\n# The TOC_EXPAND flag can be set to YES to add extra items for group members to\n# the table of contents of the HTML help documentation and to the tree view.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nTOC_EXPAND             = NO\n\n# The SITEMAP_URL tag is used to specify the full URL of the place where the\n# generated documentation will be placed on the server by the user during the\n# deployment of the documentation. The generated sitemap is called sitemap.xml\n# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL\n# is specified no sitemap is generated. For information about the sitemap\n# protocol see https://www.sitemaps.org\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nSITEMAP_URL            =\n\n# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and\n# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that\n# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help\n# (.qch) of the generated HTML documentation.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_QHP           = NO\n\n# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify\n# the file name of the resulting .qch file. The path specified is relative to\n# the HTML output folder.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQCH_FILE               =\n\n# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help\n# Project output. For more information please see Qt Help Project / Namespace\n# (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_NAMESPACE          = org.doxygen.Project\n\n# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt\n# Help Project output. For more information please see Qt Help Project / Virtual\n# Folders (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).\n# The default value is: doc.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_VIRTUAL_FOLDER     = doc\n\n# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom\n# filter to add. For more information please see Qt Help Project / Custom\n# Filters (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_NAME   =\n\n# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the\n# custom filter to add. For more information please see Qt Help Project / Custom\n# Filters (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_ATTRS  =\n\n# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this\n# project's filter section matches. Qt Help Project / Filter Attributes (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_SECT_FILTER_ATTRS  =\n\n# The QHG_LOCATION tag can be used to specify the location (absolute path\n# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to\n# run qhelpgenerator on the generated .qhp file.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHG_LOCATION           =\n\n# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be\n# generated, together with the HTML files, they form an Eclipse help plugin. To\n# install this plugin and make it available under the help contents menu in\n# Eclipse, the contents of the directory containing the HTML and XML files needs\n# to be copied into the plugins directory of eclipse. The name of the directory\n# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.\n# After copying Eclipse needs to be restarted before the help appears.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_ECLIPSEHELP   = NO\n\n# A unique identifier for the Eclipse help plugin. When installing the plugin\n# the directory name containing the HTML and XML files should also have this\n# name. Each documentation set should have its own identifier.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.\n\nECLIPSE_DOC_ID         = org.doxygen.Project\n\n# If you want full control over the layout of the generated HTML pages it might\n# be necessary to disable the index and replace it with your own. The\n# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top\n# of each HTML page. A value of NO enables the index and the value YES disables\n# it. Since the tabs in the index contain the same information as the navigation\n# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nDISABLE_INDEX          = NO\n\n# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index\n# structure should be generated to display hierarchical information. If the tag\n# value is set to YES, a side panel will be generated containing a tree-like\n# index structure (just like the one that is generated for HTML Help). For this\n# to work a browser that supports JavaScript, DHTML, CSS and frames is required\n# (i.e. any modern browser). Windows users are probably better off using the\n# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can\n# further fine tune the look of the index (see \"Fine-tuning the output\"). As an\n# example, the default style sheet generated by doxygen has an example that\n# shows how to put an image at the root of the tree instead of the PROJECT_NAME.\n# Since the tree basically has the same information as the tab index, you could\n# consider setting DISABLE_INDEX to YES when enabling this option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_TREEVIEW      = NO\n\n# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the\n# FULL_SIDEBAR option determines if the side bar is limited to only the treeview\n# area (value NO) or if it should extend to the full height of the window (value\n# YES). Setting this to YES gives a layout similar to\n# https://docs.readthedocs.io with more room for contents, but less room for the\n# project logo, title, and description. If either GENERATE_TREEVIEW or\n# DISABLE_INDEX is set to NO, this option has no effect.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFULL_SIDEBAR           = NO\n\n# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that\n# doxygen will group on one line in the generated HTML documentation.\n#\n# Note that a value of 0 will completely suppress the enum values from appearing\n# in the overview section.\n# Minimum value: 0, maximum value: 20, default value: 4.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nENUM_VALUES_PER_LINE   = 4\n\n# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used\n# to set the initial width (in pixels) of the frame in which the tree is shown.\n# Minimum value: 0, maximum value: 1500, default value: 250.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nTREEVIEW_WIDTH         = 250\n\n# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to\n# external symbols imported via tag files in a separate window.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nEXT_LINKS_IN_WINDOW    = NO\n\n# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email\n# addresses.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nOBFUSCATE_EMAILS       = YES\n\n# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg\n# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see\n# https://inkscape.org) to generate formulas as SVG images instead of PNGs for\n# the HTML output. These images will generally look nicer at scaled resolutions.\n# Possible values are: png (the default) and svg (looks nicer but requires the\n# pdf2svg or inkscape tool).\n# The default value is: png.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FORMULA_FORMAT    = png\n\n# Use this tag to change the font size of LaTeX formulas included as images in\n# the HTML documentation. When you change the font size after a successful\n# doxygen run you need to manually remove any form_*.png images from the HTML\n# output directory to force them to be regenerated.\n# Minimum value: 8, maximum value: 50, default value: 10.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_FONTSIZE       = 10\n\n# The FORMULA_MACROFILE can contain LaTeX \\newcommand and \\renewcommand commands\n# to create new LaTeX commands to be used in formulas as building blocks. See\n# the section \"Including formulas\" for details.\n\nFORMULA_MACROFILE      =\n\n# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see\n# https://www.mathjax.org) which uses client side JavaScript for the rendering\n# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX\n# installed or if you want to formulas look prettier in the HTML output. When\n# enabled you may also need to install MathJax separately and configure the path\n# to it using the MATHJAX_RELPATH option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nUSE_MATHJAX            = YES\n\n# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.\n# Note that the different versions of MathJax have different requirements with\n# regards to the different settings, so it is possible that also other MathJax\n# settings have to be changed when switching between the different MathJax\n# versions.\n# Possible values are: MathJax_2 and MathJax_3.\n# The default value is: MathJax_2.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_VERSION        = MathJax_2\n\n# When MathJax is enabled you can set the default output format to be used for\n# the MathJax output. For more details about the output format see MathJax\n# version 2 (see:\n# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3\n# (see:\n# http://docs.mathjax.org/en/latest/web/components/output.html).\n# Possible values are: HTML-CSS (which is slower, but has the best\n# compatibility. This is the name for Mathjax version 2, for MathJax version 3\n# this will be translated into chtml), NativeMML (i.e. MathML. Only supported\n# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This\n# is the name for Mathjax version 3, for MathJax version 2 this will be\n# translated into HTML-CSS) and SVG.\n# The default value is: HTML-CSS.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_FORMAT         = HTML-CSS\n\n# When MathJax is enabled you need to specify the location relative to the HTML\n# output directory using the MATHJAX_RELPATH option. The destination directory\n# should contain the MathJax.js script. For instance, if the mathjax directory\n# is located at the same level as the HTML output directory, then\n# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax\n# Content Delivery Network so you can quickly see the result without installing\n# MathJax. However, it is strongly recommended to install a local copy of\n# MathJax from https://www.mathjax.org before deployment. The default value is:\n# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2\n# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_RELPATH        = @MATHJAX_PATH@\n\n# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax\n# extension names that should be enabled during MathJax rendering. For example\n# for MathJax version 2 (see\n# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):\n# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols\n# For example for MathJax version 3 (see\n# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):\n# MATHJAX_EXTENSIONS = ams\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_EXTENSIONS     =\n\n# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces\n# of code that will be used on startup of the MathJax code. See the MathJax site\n# (see:\n# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an\n# example see the documentation.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_CODEFILE       =\n\n# When the SEARCHENGINE tag is enabled doxygen will generate a search box for\n# the HTML output. The underlying search engine uses javascript and DHTML and\n# should work on any modern browser. Note that when using HTML help\n# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)\n# there is already a search function so this one should typically be disabled.\n# For large projects the javascript based search engine can be slow, then\n# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to\n# search using the keyboard; to jump to the search box use <access key> + S\n# (what the <access key> is depends on the OS and browser, but it is typically\n# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down\n# key> to jump into the search results window, the results can be navigated\n# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel\n# the search. The filter options can be selected when the cursor is inside the\n# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>\n# to select a filter and <Enter> or <escape> to activate or cancel the filter\n# option.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nSEARCHENGINE           = YES\n\n# When the SERVER_BASED_SEARCH tag is enabled the search engine will be\n# implemented using a web server instead of a web client using JavaScript. There\n# are two flavors of web server based searching depending on the EXTERNAL_SEARCH\n# setting. When disabled, doxygen will generate a PHP script for searching and\n# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing\n# and searching needs to be provided by external tools. See the section\n# \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSERVER_BASED_SEARCH    = NO\n\n# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP\n# script for searching. Instead the search results are written to an XML file\n# which needs to be processed by an external indexer. Doxygen will invoke an\n# external search engine pointed to by the SEARCHENGINE_URL option to obtain the\n# search results.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see:\n# https://xapian.org/).\n#\n# See the section \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH        = NO\n\n# The SEARCHENGINE_URL should point to a search engine hosted by a web server\n# which will return the search results when EXTERNAL_SEARCH is enabled.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see:\n# https://xapian.org/). See the section \"External Indexing and Searching\" for\n# details.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHENGINE_URL       =\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed\n# search data is written to a file for indexing by an external tool. With the\n# SEARCHDATA_FILE tag the name of this file can be specified.\n# The default file is: searchdata.xml.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHDATA_FILE        = searchdata.xml\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the\n# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is\n# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple\n# projects and redirect the results back to the right project.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH_ID     =\n\n# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen\n# projects other than the one defined by this configuration file, but that are\n# all added to the same external search index. Each project needs to have a\n# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of\n# to a relative location where the documentation can be found. The format is:\n# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTRA_SEARCH_MAPPINGS  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the LaTeX output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.\n# The default value is: YES.\n\nGENERATE_LATEX         = NO\n\n# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: latex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_OUTPUT           = latex\n\n# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be\n# invoked.\n#\n# Note that when not enabling USE_PDFLATEX the default is latex when enabling\n# USE_PDFLATEX the default is pdflatex and when in the later case latex is\n# chosen this is overwritten by pdflatex. For specific output languages the\n# default can have been set differently, this depends on the implementation of\n# the output language.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_CMD_NAME         =\n\n# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate\n# index for LaTeX.\n# Note: This tag is used in the Makefile / make.bat.\n# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file\n# (.tex).\n# The default file is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nMAKEINDEX_CMD_NAME     = makeindex\n\n# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to\n# generate index for LaTeX. In case there is no backslash (\\) as first character\n# it will be automatically added in the LaTeX code.\n# Note: This tag is used in the generated output file (.tex).\n# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.\n# The default value is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_MAKEINDEX_CMD    = makeindex\n\n# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nCOMPACT_LATEX          = NO\n\n# The PAPER_TYPE tag can be used to set the paper type that is used by the\n# printer.\n# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x\n# 14 inches) and executive (7.25 x 10.5 inches).\n# The default value is: a4.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPAPER_TYPE             = a4\n\n# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names\n# that should be included in the LaTeX output. The package can be specified just\n# by its name or with the correct syntax as to be used with the LaTeX\n# \\usepackage command. To get the times font for instance you can specify :\n# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}\n# To use the option intlimits with the amsmath package you can specify:\n# EXTRA_PACKAGES=[intlimits]{amsmath}\n# If left blank no extra packages will be included.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nEXTRA_PACKAGES         = amsmath \\\n                         amsfonts \\\n                         amssymb\n\n# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for\n# the generated LaTeX document. The header should contain everything until the\n# first chapter. If it is left blank doxygen will generate a standard header. It\n# is highly recommended to start with a default header using\n# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty\n# and then modify the file new_header.tex. See also section \"Doxygen usage\" for\n# information on how to generate the default header that doxygen normally uses.\n#\n# Note: Only use a user-defined header if you know what you are doing!\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of doxygen. The following\n# commands have a special meaning inside the header (and footer): For a\n# description of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HEADER           =\n\n# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for\n# the generated LaTeX document. The footer should contain everything after the\n# last chapter. If it is left blank doxygen will generate a standard footer. See\n# LATEX_HEADER for more information on how to generate a default footer and what\n# special commands can be used inside the footer. See also section \"Doxygen\n# usage\" for information on how to generate the default footer that doxygen\n# normally uses. Note: Only use a user-defined footer if you know what you are\n# doing!\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_FOOTER           =\n\n# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# LaTeX style sheets that are included after the standard style sheets created\n# by doxygen. Using this option one can overrule certain style aspects. Doxygen\n# will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list).\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_STYLESHEET =\n\n# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the LATEX_OUTPUT output\n# directory. Note that the files will be copied as-is; there are no commands or\n# markers available.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_FILES      =\n\n# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is\n# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will\n# contain links (just like the HTML output) instead of page references. This\n# makes the output suitable for online browsing using a PDF viewer.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPDF_HYPERLINKS         = YES\n\n# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as\n# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX\n# files. Set this option to YES, to get a higher quality PDF documentation.\n#\n# See also section LATEX_CMD_NAME for selecting the engine.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nUSE_PDFLATEX           = YES\n\n# The LATEX_BATCHMODE tag ignals the behavior of LaTeX in case of an error.\n# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch\n# mode nothing is printed on the terminal, errors are scrolled as if <return> is\n# hit at every error; missing files that TeX tries to input or request from\n# keyboard input (\\read on a not open input stream) cause the job to abort,\n# NON_STOP In nonstop mode the diagnostic message will appear on the terminal,\n# but there is no possibility of user interaction just like in batch mode,\n# SCROLL In scroll mode, TeX will stop only for missing files to input or if\n# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at\n# each error, asking for user intervention.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BATCHMODE        = NO\n\n# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the\n# index chapters (such as File Index, Compound Index, etc.) in the output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HIDE_INDICES     = NO\n\n# The LATEX_BIB_STYLE tag can be used to specify the style to use for the\n# bibliography, e.g. plainnat, or ieeetr. See\n# https://en.wikipedia.org/wiki/BibTeX and \\cite for more info.\n# The default value is: plain.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BIB_STYLE        = plain\n\n# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)\n# path from which the emoji images will be read. If a relative path is entered,\n# it will be relative to the LATEX_OUTPUT directory. If left blank the\n# LATEX_OUTPUT directory will be used.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EMOJI_DIRECTORY  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the RTF output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The\n# RTF output is optimized for Word 97 and may not look too pretty with other RTF\n# readers/editors.\n# The default value is: NO.\n\nGENERATE_RTF           = NO\n\n# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: rtf.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_OUTPUT             = rtf\n\n# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nCOMPACT_RTF            = NO\n\n# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will\n# contain hyperlink fields. The RTF file will contain links (just like the HTML\n# output) instead of page references. This makes the output suitable for online\n# browsing using Word or some other Word compatible readers that support those\n# fields.\n#\n# Note: WordPad (write) and others do not support links.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_HYPERLINKS         = NO\n\n# Load stylesheet definitions from file. Syntax is similar to doxygen's\n# configuration file, i.e. a series of assignments. You only have to provide\n# replacements, missing definitions are set to their default value.\n#\n# See also section \"Doxygen usage\" for information on how to generate the\n# default style sheet that doxygen normally uses.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_STYLESHEET_FILE    =\n\n# Set optional variables used in the generation of an RTF document. Syntax is\n# similar to doxygen's configuration file. A template extensions file can be\n# generated using doxygen -e rtf extensionFile.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_EXTENSIONS_FILE    =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the man page output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for\n# classes and files.\n# The default value is: NO.\n\nGENERATE_MAN           = YES\n\n# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it. A directory man3 will be created inside the directory specified by\n# MAN_OUTPUT.\n# The default directory is: man.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_OUTPUT             = man\n\n# The MAN_EXTENSION tag determines the extension that is added to the generated\n# man pages. In case the manual section does not start with a number, the number\n# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is\n# optional.\n# The default value is: .3.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_EXTENSION          = .3\n\n# The MAN_SUBDIR tag determines the name of the directory created within\n# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by\n# MAN_EXTENSION with the initial . removed.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_SUBDIR             =\n\n# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it\n# will generate one additional man file for each entity documented in the real\n# man page(s). These additional files only source the real man page, but without\n# them the man command would be unable to find the correct page.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_LINKS              = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the XML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that\n# captures the structure of the code including all documentation.\n# The default value is: NO.\n\nGENERATE_XML           = NO\n\n# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: xml.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_OUTPUT             = xml\n\n# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program\n# listings (including syntax highlighting and cross-referencing information) to\n# the XML output. Note that enabling this will significantly increase the size\n# of the XML output.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_PROGRAMLISTING     = YES\n\n# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include\n# namespace members in file scope as well, matching the HTML output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_NS_MEMB_FILE_SCOPE = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the DOCBOOK output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files\n# that can be used to generate PDF.\n# The default value is: NO.\n\nGENERATE_DOCBOOK       = NO\n\n# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in\n# front of it.\n# The default directory is: docbook.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_OUTPUT         = docbook\n\n#---------------------------------------------------------------------------\n# Configuration options for the AutoGen Definitions output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an\n# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures\n# the structure of the code including all documentation. Note that this feature\n# is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_AUTOGEN_DEF   = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the Perl module output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module\n# file that captures the structure of the code including all documentation.\n#\n# Note that this feature is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_PERLMOD       = NO\n\n# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary\n# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI\n# output from the Perl module output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_LATEX          = NO\n\n# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely\n# formatted so it can be parsed by a human reader. This is useful if you want to\n# understand what is going on. On the other hand, if this tag is set to NO, the\n# size of the Perl module output will be much smaller and Perl will parse it\n# just the same.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_PRETTY         = YES\n\n# The names of the make variables in the generated doxyrules.make file are\n# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful\n# so different doxyrules.make files included by the same Makefile don't\n# overwrite each other's variables.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_MAKEVAR_PREFIX =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor\n#---------------------------------------------------------------------------\n\n# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all\n# C-preprocessor directives found in the sources and include files.\n# The default value is: YES.\n\nENABLE_PREPROCESSING   = YES\n\n# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names\n# in the source code. If set to NO, only conditional compilation will be\n# performed. Macro expansion can be done in a controlled way by setting\n# EXPAND_ONLY_PREDEF to YES.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nMACRO_EXPANSION        = YES\n\n# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then\n# the macro expansion is limited to the macros specified with the PREDEFINED and\n# EXPAND_AS_DEFINED tags.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_ONLY_PREDEF     = YES\n\n# If the SEARCH_INCLUDES tag is set to YES, the include files in the\n# INCLUDE_PATH will be searched if a #include is found.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSEARCH_INCLUDES        = YES\n\n# The INCLUDE_PATH tag can be used to specify one or more directories that\n# contain include files that are not input files but should be processed by the\n# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of\n# RECURSIVE has no effect here.\n# This tag requires that the tag SEARCH_INCLUDES is set to YES.\n\nINCLUDE_PATH           =\n\n# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard\n# patterns (like *.h and *.hpp) to filter out the header-files in the\n# directories. If left blank, the patterns specified with FILE_PATTERNS will be\n# used.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nINCLUDE_FILE_PATTERNS  =\n\n# The PREDEFINED tag can be used to specify one or more macro names that are\n# defined before the preprocessor is started (similar to the -D option of e.g.\n# gcc). The argument of the tag is a list of macros of the form: name or\n# name=definition (no spaces). If the definition and the \"=\" are omitted, \"=1\"\n# is assumed. To prevent a macro definition from being undefined via #undef or\n# recursively expanded use the := operator instead of the = operator.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nPREDEFINED             = DOXYGEN=1 \\\n                         DEBUG=1 \\\n                         DEAL_II_WITH_GSL=1 \\\n                         decltype(B)=auto\n\n# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this\n# tag can be used to specify a list of macro names that should be expanded. The\n# macro definition that is found in the sources will be used. Use the PREDEFINED\n# tag if you want to use a different macro definition that overrules the\n# definition found in the source code.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_AS_DEFINED      = ACCESSOR_READ_ONLY \\\n                         ACCESSOR_READ_ONLY_NO_DEREFERENCE\n\n# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will\n# remove all references to function-like macros that are alone on a line, have\n# an all uppercase name, and do not end with a semicolon. Such function macros\n# are typically used for boiler-plate code, and will confuse the parser if not\n# removed.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSKIP_FUNCTION_MACROS   = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to external references\n#---------------------------------------------------------------------------\n\n# The TAGFILES tag can be used to specify one or more tag files. For each tag\n# file the location of the external documentation should be added. The format of\n# a tag file without this location is as follows:\n# TAGFILES = file1 file2 ...\n# Adding location for the tag files is done as follows:\n# TAGFILES = file1=loc1 \"file2 = loc2\" ...\n# where loc1 and loc2 can be relative or absolute paths or URLs. See the\n# section \"Linking to external documentation\" for more information about the use\n# of tag files.\n# Note: Each tag file must have a unique name (where the name does NOT include\n# the path). If a tag file is not located in the directory in which doxygen is\n# run, you must also specify the path to the tagfile here.\n\nTAGFILES               =\n\n# When a file name is specified after GENERATE_TAGFILE, doxygen will create a\n# tag file that is based on the input files it reads. See section \"Linking to\n# external documentation\" for more information about the usage of tag files.\n\nGENERATE_TAGFILE       = ryujin.tag\n\n# If the ALLEXTERNALS tag is set to YES, all external class will be listed in\n# the class index. If set to NO, only the inherited external classes will be\n# listed.\n# The default value is: NO.\n\nALLEXTERNALS           = NO\n\n# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed\n# in the modules index. If set to NO, only the current project's groups will be\n# listed.\n# The default value is: YES.\n\nEXTERNAL_GROUPS        = YES\n\n# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in\n# the related pages index. If set to NO, only the current project's pages will\n# be listed.\n# The default value is: YES.\n\nEXTERNAL_PAGES         = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to diagram generator tools\n#---------------------------------------------------------------------------\n\n# If set to YES the inheritance and collaboration graphs will hide inheritance\n# and usage relations if the target is undocumented or is not a class.\n# The default value is: YES.\n\nHIDE_UNDOC_RELATIONS   = YES\n\n# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is\n# available from the path. This tool is part of Graphviz (see:\n# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent\n# Bell Labs. The other options in this section have no effect if this option is\n# set to NO\n# The default value is: NO.\n\nHAVE_DOT               = YES\n\n# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed\n# to run in parallel. When set to 0 doxygen will base this on the number of\n# processors available in the system. You can set it explicitly to a value\n# larger than 0 to get control over the balance between CPU load and processing\n# speed.\n# Minimum value: 0, maximum value: 32, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_NUM_THREADS        = 0\n\n# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of\n# subgraphs. When you want a differently looking font in the dot files that\n# doxygen generates you can specify fontname, fontcolor and fontsize attributes.\n# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,\n# Edge and Graph Attributes specification</a> You need to make sure dot is able\n# to find the font, which can be done by putting it in a standard location or by\n# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the\n# directory containing the font. Default graphviz fontsize is 14.\n# The default value is: fontname=Helvetica,fontsize=10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_COMMON_ATTR        = \"fontname=Helvetica,fontsize=10\"\n\n# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can\n# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a\n# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about\n# arrows shapes.</a>\n# The default value is: labelfontname=Helvetica,labelfontsize=10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_EDGE_ATTR          = \"labelfontname=Helvetica,labelfontsize=10\"\n\n# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes\n# around nodes set 'shape=plain' or 'shape=plaintext' <a\n# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>\n# The default value is: shape=box,height=0.2,width=0.4.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_NODE_ATTR          = \"shape=box,height=0.2,width=0.4\"\n\n# You can set the path where dot can find font specified with fontname in\n# DOT_COMMON_ATTR and others dot attributes.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTPATH           =\n\n# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will\n# generate a graph for each documented class showing the direct and indirect\n# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and\n# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case\n# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the\n# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used.\n# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance\n# relations will be shown as texts / links.\n# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN.\n# The default value is: YES.\n\nCLASS_GRAPH            = YES\n\n# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a\n# graph for each documented class showing the direct and indirect implementation\n# dependencies (inheritance, containment, and class references variables) of the\n# class with other documented classes.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCOLLABORATION_GRAPH    = YES\n\n# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for\n# groups, showing the direct groups dependencies. See also the chapter Grouping\n# in the manual.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGROUP_GRAPHS           = YES\n\n# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and\n# collaboration diagrams in a style similar to the OMG's Unified Modeling\n# Language.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LOOK               = NO\n\n# If the UML_LOOK tag is enabled, the fields and methods are shown inside the\n# class node. If there are many fields or methods and many nodes the graph may\n# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the\n# number of items for each type to make the size more manageable. Set this to 0\n# for no limit. Note that the threshold may be exceeded by 50% before the limit\n# is enforced. So when you set the threshold to 10, up to 15 fields may appear,\n# but if the number exceeds 15, the total amount of fields shown is limited to\n# 10.\n# Minimum value: 0, maximum value: 100, default value: 10.\n# This tag requires that the tag UML_LOOK is set to YES.\n\nUML_LIMIT_NUM_FIELDS   = 10\n\n# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and\n# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS\n# tag is set to YES, doxygen will add type and arguments for attributes and\n# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen\n# will not generate fields with class member information in the UML graphs. The\n# class diagrams will look similar to the default class diagrams but using UML\n# notation for the relationships.\n# Possible values are: NO, YES and NONE.\n# The default value is: NO.\n# This tag requires that the tag UML_LOOK is set to YES.\n\nDOT_UML_DETAILS        = NO\n\n# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters\n# to display on a single line. If the actual line length exceeds this threshold\n# significantly it will wrapped across multiple lines. Some heuristics are apply\n# to avoid ugly line breaks.\n# Minimum value: 0, maximum value: 1000, default value: 17.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_WRAP_THRESHOLD     = 17\n\n# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and\n# collaboration graphs will show the relations between templates and their\n# instances.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nTEMPLATE_RELATIONS     = NO\n\n# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to\n# YES then doxygen will generate a graph for each documented file showing the\n# direct and indirect include dependencies of the file with other documented\n# files.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDE_GRAPH          = YES\n\n# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are\n# set to YES then doxygen will generate a graph for each documented file showing\n# the direct and indirect include dependencies of the file with other documented\n# files.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDED_BY_GRAPH      = YES\n\n# If the CALL_GRAPH tag is set to YES then doxygen will generate a call\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable call graphs for selected\n# functions only using the \\callgraph command. Disabling a call graph can be\n# accomplished by means of the command \\hidecallgraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALL_GRAPH             = NO\n\n# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable caller graphs for selected\n# functions only using the \\callergraph command. Disabling a caller graph can be\n# accomplished by means of the command \\hidecallergraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALLER_GRAPH           = NO\n\n# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical\n# hierarchy of all classes instead of a textual one.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGRAPHICAL_HIERARCHY    = YES\n\n# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the\n# dependencies a directory has on other directories in a graphical way. The\n# dependency relations are determined by the #include relations between the\n# files in the directories.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDIRECTORY_GRAPH        = YES\n\n# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels\n# of child directories generated in directory dependency graphs by dot.\n# Minimum value: 1, maximum value: 25, default value: 1.\n# This tag requires that the tag DIRECTORY_GRAPH is set to YES.\n\nDIR_GRAPH_MAX_DEPTH    = 1\n\n# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images\n# generated by dot. For an explanation of the image formats see the section\n# output formats in the documentation of the dot tool (Graphviz (see:\n# https://www.graphviz.org/)).\n# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order\n# to make the SVG files visible in IE 9+ (other browsers do not have this\n# requirement).\n# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,\n# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and\n# png:gdiplus:gdiplus.\n# The default value is: png.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_IMAGE_FORMAT       = png\n\n# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to\n# enable generation of interactive SVG images that allow zooming and panning.\n#\n# Note that this requires a modern browser other than Internet Explorer. Tested\n# and working are Firefox, Chrome, Safari, and Opera.\n# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make\n# the SVG files visible. Older versions of IE do not have SVG support.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINTERACTIVE_SVG        = NO\n\n# The DOT_PATH tag can be used to specify the path where the dot tool can be\n# found. If left blank, it is assumed the dot tool can be found in the path.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_PATH               =\n\n# The DOTFILE_DIRS tag can be used to specify one or more directories that\n# contain dot files that are included in the documentation (see the \\dotfile\n# command).\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOTFILE_DIRS           =\n\n# You can include diagrams made with dia in doxygen documentation. Doxygen will\n# then run dia to produce the diagram and insert it in the documentation. The\n# DIA_PATH tag allows you to specify the directory where the dia binary resides.\n# If left empty dia is assumed to be found in the default search path.\n\nDIA_PATH               =\n\n# The DIAFILE_DIRS tag can be used to specify one or more directories that\n# contain dia files that are included in the documentation (see the \\diafile\n# command).\n\nDIAFILE_DIRS           =\n\n# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the\n# path where java can find the plantuml.jar file or to the filename of jar file\n# to be used. If left blank, it is assumed PlantUML is not used or called during\n# a preprocessing step. Doxygen will generate a warning when it encounters a\n# \\startuml command in this case and will not generate output for the diagram.\n\nPLANTUML_JAR_PATH      =\n\n# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a\n# configuration file for plantuml.\n\nPLANTUML_CFG_FILE      =\n\n# When using plantuml, the specified paths are searched for files specified by\n# the !include statement in a plantuml block.\n\nPLANTUML_INCLUDE_PATH  =\n\n# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes\n# that will be shown in the graph. If the number of nodes in a graph becomes\n# larger than this value, doxygen will truncate the graph, which is visualized\n# by representing a node as a red box. Note that doxygen if the number of direct\n# children of the root node in a graph is already larger than\n# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that\n# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.\n# Minimum value: 0, maximum value: 10000, default value: 50.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_GRAPH_MAX_NODES    = 50\n\n# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs\n# generated by dot. A depth value of 3 means that only nodes reachable from the\n# root by following a path via at most 3 edges will be shown. Nodes that lay\n# further from the root node will be omitted. Note that setting this option to 1\n# or 2 may greatly reduce the computation time needed for large code bases. Also\n# note that the size of a graph can be further restricted by\n# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.\n# Minimum value: 0, maximum value: 1000, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nMAX_DOT_GRAPH_DEPTH    = 2\n\n# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output\n# files in one run (i.e. multiple -o and -T options on the command line). This\n# makes dot run faster, but since only newer versions of dot (>1.8.10) support\n# this, this feature is disabled by default.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_MULTI_TARGETS      = NO\n\n# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page\n# explaining the meaning of the various boxes and arrows in the dot generated\n# graphs.\n# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal\n# graphical representation for inheritance and collaboration diagrams is used.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGENERATE_LEGEND        = YES\n\n# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate\n# files that are used to generate the various graphs.\n#\n# Note: This setting is not only used for dot files but also for msc temporary\n# files.\n# The default value is: YES.\n\nDOT_CLEANUP            = YES\n\n# You can define message sequence charts within doxygen comments using the \\msc\n# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will\n# use a built-in version of mscgen tool to produce the charts. Alternatively,\n# the MSCGEN_TOOL tag can also specify the name an external tool. For instance,\n# specifying prog as the value, doxygen will call the tool as prog -T\n# <outfile_format> -o <outputfile> <inputfile>. The external tool should support\n# output file formats \"png\", \"eps\", \"svg\", and \"ismap\".\n\nMSCGEN_TOOL            =\n\n# The MSCFILE_DIRS tag can be used to specify one or more directories that\n# contain msc files that are included in the documentation (see the \\mscfile\n# command).\n\nMSCFILE_DIRS           =\n"
  },
  {
    "path": "doc/references.bib",
    "content": "%\n% Background:\n%\n\n@article{Becker1922,\n  author  = {Becker, R.},\n  title   = {Sto{\\ss}welle und Detonation},\n  journal = {Zeitschrift fur Physik},\n  year    = {1922},\n  volume  = {8},\n  number  = {1},\n  pages   = {321-362}\n}\n\n@article{Brooks1982,\n  author  = {Brooks, Alexander N. and Hughes, Thomas J. R.},\n  title   = {Streamline upwind/{P}etrov-{G}alerkin formulations for convection dominated flows with particular emphasis on the incompressible {N}avier-{S}tokes equations},\n  note    = {FENOMECH ''81, Part I (Stuttgart, 1981)},\n  journal = {Computer Methods in Applied Mechanics and Engineering},\n  volume  = {32},\n  year    = {1982},\n  number  = {1-3},\n  pages   = {199--259},\n  doi     = {10.1016/0045-7825(82)90071-8}\n}\n\n@article{Chertock2015,\n  author     = {Chertock, A. and Cui, S. and Kurganov, A. and Wu, T.},\n  title      = {Well-balanced positivity preserving central-upwind scheme for\n                the shallow water system with friction terms},\n  journal    = {Internat. J. Numer. Methods Fluids},\n  fjournal   = {International Journal for Numerical Methods in Fluids},\n  volume     = {78},\n  year       = {2015},\n  number     = {6},\n  pages      = {355--383},\n  optissn    = {0271-2091},\n  mrclass    = {65M06 (76D05 76M20 86A05 86A10)},\n  mrnumber   = {3350178},\n  mrreviewer = {Jean-Pierre Croisille},\n  optdoi     = {10.1002/fld.4023},\n  opturl     = {http://dx.doi.org/10.1002/fld.4023}\n}\n\n@phdthesis{clayton2023robust,\n  title={A Robust Second Order Invariant-Domain Preserving Approximation of the Compressible Euler Equations with an Arbitrary Equation of State},\n  author={Clayton, Bennett Giles},\n  year={2023}\n}\n\n@article{ClaytonGuermondPopov-2022,\n  author  = {Clayton, Bennett and Guermond, Jean-Luc and Popov, Bojan},\n  title   = {Invariant Domain-Preserving Approximations for the {E}uler Equations with Tabulated Equation of State},\n  journal = {SIAM Journal on Scientific Computing},\n  volume  = {44},\n  number  = {1},\n  pages   = {A444-A470},\n  year    = {2022}\n}\n\n@book{ErnGuermond2004,\n  author    = {Ern, Alexandre and Guermond, Jean-Luc},\n  title     = {Theory and practice of finite elements},\n  series    = {Applied Mathematical Sciences},\n  volume    = {159},\n  publisher = {Springer-Verlag, New York},\n  year      = {2004},\n  pages     = {xiv+524},\n  doi       = {10.1007/978-1-4757-4355-5}\n}\n\n@article{GuermondEtAl2011,\n  author  = {Guermond, Jean-Luc and Pasquetti, Richard and Popov, Bojan},\n  title   = {Entropy viscosity method for nonlinear conservation laws},\n  journal = {Journal of Computational Physics},\n  volume  = {230},\n  year    = {2011},\n  number  = {11},\n  pages   = {4248--4267},\n  doi     = {10.1016/j.jcp.2010.11.043}\n}\n\n@article{GuermondPopov2016,\n  author  = {Guermond, Jean-Luc and Popov, Bojan},\n  title   = {Invariant domains and first-order continuous finite element approximation for hyperbolic systems},\n  journal = {SIAM Journal on Numerical Analysis},\n  volume  = {54},\n  year    = {2016},\n  number  = {4},\n  pages   = {2466--2489},\n  doi     = {10.1137/16M1074291}\n}\n\n@article{GuermondPopov2016b,\n  author  = {Guermond, Jean-Luc and Popov, Bojan},\n  title   = {Fast estimation of the maximum wave speed in the Riemann problem for the Euler equations},\n  journal = {Journal of Computational Physics},\n  volume  = {321},\n  year    = {2016},\n  pages   = {908--926},\n  doi     = {10.1016/j.jcp.2016.05.054}\n}\n\n@article{GuermondEtAl2018,\n  author  = {Guermond, Jean-Luc and Nazarov, Murtazo and Popov, Bojan and Tomas, Ignacio},\n  title   = {Second-order invariant domain preserving approximation of the {E}uler equations using convex limiting},\n  journal = {SIAM Journal on Scientific Computing},\n  volume  = {40},\n  year    = {2018},\n  number  = {5},\n  pages   = {A3211--A3239},\n  doi     = {10.1137/17M1149961}\n}\n\n@article{GuermondEtAl2018SW,\n  author  = {Guermond, Jean-Luc and de Luna, Manuel Quezada and Popov, Bojan and Kees, Christopher E. and Farthing, Matthew W.},\n  title   = {Well-Balanced Second-Order Finite Element Approximation of the Shallow Water Equations with Friction},\n  journal = {SIAM Journal on Scientific Computing},\n  volume  = {40},\n  year    = {2018},\n  number  = {6},\n  pages   = {A3873-A3901},\n  doi     = {10.1137/17M1156162}\n}\n\n@book{ErnGuermond2021,\n  author    = {Ern, Alexandre and Guermond, Jean-Luc},\n  title     = {Finite Elements {III}. First-Order and Time-Dependent {PDE}s},\n  series    = {Texts in Applied Mathematics},\n  volume    = {74},\n  publisher = {Springer-Verlag, New York},\n  year      = {2021},\n  pages     = {v+417},\n  doi       = {10.1007/978-3-030-57348-5}\n}\n\n@article{ErnGuermond2023,\n  title={Invariant-domain preserving high-order time stepping: II. IMEX schemes},\n  author={Ern, Alexandre and Guermond, Jean-Luc},\n  journal={SIAM Journal on Scientific Computing},\n  volume={45},\n  number={5},\n  pages={A2511--A2538},\n  year={2023},\n  publisher={SIAM}\n}\n\n@article{hou2013,\n  title     = {A robust well-balanced model on unstructured grids for shallow water flows with wetting and drying over complex topography},\n  author    = {Hou, Jingming and Simons, Franz and Mahgoub, Mohamed and Hinkelmann, Reinhard},\n  journal   = {Computer methods in applied mechanics and engineering},\n  volume    = {257},\n  pages     = {126--149},\n  year      = {2013},\n  publisher = {Elsevier}\n}\n\n@article{Johnson1986,\n  author  = {Johnson, C. and Pitk\\\"{a}ranta, J.},\n  title   = {An analysis of the discontinuous {G}alerkin method for a scalar hyperbolic equation},\n  journal = {Mathematics of Computation},\n  volume  = {46},\n  year    = {1986},\n  number  = {173},\n  pages   = {1--26},\n  doi     = {10.2307/2008211}\n}\n\n@article{Jameson2017,\n  year    = {2017},\n  author  = {Antony Jameson},\n  title   = {Origins and further development of the {J}ameson-{S}chmidt-{T}urkel scheme},\n  journal = {AIAA J.},\n  volume  = {55},\n  pages   = {1487--1510},\n  doi     = {10.2514/1.J055493}\n}\n\n@article{Martinez2018,\n  author  = {S. Martínez-Aranda and J. Fernández-Pato and D. Caviedes-Voullième and I. García-Palacín and P. García-Navarro}\n  title   = {Towards transient experimental water surfaces: A new benchmark dataset for 2D shallow water solvers},\n  journal = {Advances in Water Resources},\n  volume  = {121},\n  pages   = {130-149},\n  year    = {2018},\n  issn    = {0309-1708},\n  doi     = {https://doi.org/10.1016/j.advwatres.2018.08.013},\n}\n\n@article{Noh1987,\n  author  = {Noh, W.F.},\n  title   = {Errors for calculations of strong shocks using an\n             artificial viscosity and an artifiscial heat flux},\n  journal = {Journal of Computational Physics},\n  year    = {1987},\n  volume  = {72},\n  number  = {1},\n  pages   = {78-120}\n}\n\n@book{Rainald2008,\n  doi       = {10.2307/2008211},\n  author    = {Lohner, Rainald},\n  title     = {Edge-Based Compressible Flow Solvers},\n  booktitle = {Applied Computational Fluid Dynamics Techniques},\n  chapter   = {10},\n  pages     = {187--200},\n  doi       = {10.1002/9780470989746.ch10},\n  year      = {2008}\n}\n\n@article{Ricchiuto_Bollermann_2009,\n  author   = {Ricchiuto, Mario and Bollermann, Andreas},\n  title    = {Stabilized residual distribution for shallow water\n              simulations},\n  journal  = {J. Comput. Phys.},\n  fjournal = {Journal of Computational Physics},\n  volume   = {228},\n  year     = {2009},\n  number   = {4},\n  pages    = {1071--1115}\n}\n\n@article{Shu1988,\n  title   = {Efficient implementation of essentially non-oscillatory shock-capturing schemes},\n  author  = {Chi-Wang Shu and Stanley Osher},\n  journal = {Journal of Computational Physics},\n  volume  = {77},\n  number  = {2},\n  pages   = {439 - 471},\n  year    = {1988},\n  doi     = {10.1016/0021-9991(88)90177-5}\n}\n\n@article{swashes_2013,\n  title     = {SWASHES: a compilation of shallow water analytic solutions for hydraulic and environmental studies},\n  author    = {Delestre, Olivier and Lucas, Carine and Ksinant, Pierre-Antoine and Darboux, Fr{\\'e}d{\\'e}ric and Laguerre, Christian and Vo, T-N-Tuoi and James, Francois and Cordier, St{\\'e}phane},\n  journal   = {International Journal for Numerical Methods in Fluids},\n  volume    = {72},\n  number    = {3},\n  pages     = {269--300},\n  year      = {2013},\n  publisher = {Wiley Online Library}\n}\n\n\n\n@book{Toro2009,\n  year      = {2008},\n  author    = {Eleuterio F. Toro},\n  publisher = {Springer-Verlag, Berlin, Heidelberg},\n  title     = {Riemann Solvers and Numerical Methods for Fluid Dynamics},\n  doi       = {10.1007/b79761},\n  year      = {2009}\n}\n\n@article{Woodward1984,\n  author  = {Woodward, P. and Colella, P.},\n  title   = {The numerical simulation of two-dimensional fluid flow\n             with strong shocks},\n  journal = {Journal of Computational Physics},\n  year    = {1984},\n  volume  = {54},\n  pages   = {115-173}\n}\n\n@article{Zhang-Shu-2010,\n  title     = {On positivity-preserving high order discontinuous Galerkin schemes for compressible Euler equations on rectangular meshes},\n  author    = {Zhang, Xiangxiong and Shu, Chi-Wang},\n  journal   = {Journal of Computational Physics},\n  volume    = {229},\n  number    = {23},\n  pages     = {8918--8934},\n  year      = {2010},\n  publisher = {Elsevier}\n}\n\n%\n% Ryujin related:\n%\n\n@article{ryujin-2021-1,\n  author  = {Matthias Maier and Martin Kronbichler},\n  title   = {Efficient parallel 3D computation of the compressible Euler equations with an invariant-domain preserving second-order finite-element scheme},\n  doi     = {10.1145/3470637},\n  url     = {https://arxiv.org/abs/2007.00094},\n  journal = {ACM Transactions on Parallel Computing},\n  year    = {2021},\n  volume  = {8},\n  number  = {3},\n  pages   = {16:1-30}\n}\n\n@article{ryujin-2021-2,\n  author  = {Jean-Luc Guermond and Matthias Maier and Bojan Popov and Ignacio Tomas},\n  title   = {Second-order invariant domain preserving approximation of the compressible Navier--Stokes equations},\n  doi     = {10.1016/j.cma.2020.113608},\n  url     = {https://arxiv.org/abs/2009.06022},\n  journal = {Computer Methods in Applied Mechanics and Engineering},\n  year    = {2021},\n  volume  = {375},\n  number  = {1},\n  pages   = {113608}\n}\n\n@article{ryujin-2021-3,\n  author  = {Jean-Luc~Guermond and Martin Kronbichler and Matthias Maier and Bojan Popov and Ignacio Tomas},\n  title   = {On the implementation of a robust and efficient finite element-based parallel solver for the compressible Navier-stokes equations},\n  url     = {https://arxiv.org/abs/2106.02159},\n  journal = {Computer Methods in Applied Mechanics and Engineering},\n  year    = {2022},\n  volume  = {389},\n  pages   = {114250}\n}\n\n@article{ryujin-2023-4,\n  author  = {Bennett Clayton and Jean-Luc Guermond and Matthias Maier and Bojan Popov and Tovar, Eric J.},\n  title   = {Robust second-order approximation of the compressible Euler equations with an arbitrary equation of state},\n  url     = {http://arxiv.org/abs/2207.12832},\n  journal = {Journal of Computational Physics},\n  pages   = {111926},\n  year    = {2023}\n}\n\n@article{ryujin-2024-5,\n  author = {Jean-Luc Guermond and Matthias Maier and Bojan Popov and Laura Saavedra and Ignacio Tomas},\n  title = {First-Order Greedy Invariant-Domain Preserving Approximation for Hyperbolic Problems: Scalar Conservation Laws, and p-System},\n  url = {https://arxiv.org/abs/2310.01713},\n  journal = {Journal of Scientific Computing},\n  year = {2024},\n  volume = {100},\n  number = {46},\n  pages = {},\n}\n\n@article{ryujin-2025-6,\n  author = {Jean-Luc Guermond and Matthias Maier and Tovar, Eric J.},\n  title = {A high-order explicit Runge-Kutta approximation technique for the shallow water equations},\n  url = {https://arxiv.org/abs/2403.17123},\n  journal = {Computers \\& Fluids},\n  year = {2025},\n  volume = {288},\n  pages = {106493},\n}\n\n@article{ryujin-2025-7,\n  author = {Martin Kronbichler and Matthias Maier and Ignacio Tomas},\n  title = {Graph-based methods for hyperbolic systems of conservation laws using discontinuous space discretizations},\n  url = {https://arxiv.org/abs/2402.04514},\n  journal = {Communications in Computational Physics},\n  year = {2025},\n  volume = {38},\n  pages = {74--108}\n}\n\n@article{ryujin-2025-8,\n  author = {Jake Harmon and Martin Kronbichler and Matthias Maier and Eric Tovar},\n  title = {A conservative invariant-domain preserving projection technique for hyperbolic systems under adaptive mesh refinement},\n  url = {https://arxiv.org/abs/2507.18717},\n  year = {2025},\n  journal = {submitted}\n}\n"
  },
  {
    "path": "prm/README.md",
    "content": "<img align=\"right\" height=\"150\" src=\"../doc/logo.png\">\n\nParameter files\n===============\n\nThis directory contains a collection of parameter files ranging from\nairfoils, to various published benchmark configurations to validation\nconfigurations with test vectors.\n\n\nVerification\n------------\n\nThe `./verification` subdirectory contains parameter files and\n<i>baseline</i> output vectors that document expected error and convergence\nrates for various analytical solutions. All configurations compare the\nsimulation result at final time to a known analytic solution and record the\nnormalized L1, L2, and L\\infty error norms (summed up over all components).\n\nAll test configurations should be run with double floating point precision.\n\nBenchmarks\n----------\n\nThe `./benchmarks` directory contains parameter files for well known and\npopular <i>benchmark</i> configurations. These configurations typically do\nnot have an analytical solution, but the expected solution structure is\nwell known. They are thus usually compared in the <i>eyeball norm</i>.\n"
  },
  {
    "path": "prm/benchmarks/euler-mach10-double-mach-reflection.prm",
    "content": "##\n#\n# The Mach 10 double mach reflection benchmark as described in [1].\n#\n# The chosen refinement level 7 corresponds to around 1.87M gridpoints\n# (7.48M degrees of freedom), which requires around 3k time steps and runs\n# for about (4h / #n_cores) on a modern system.\n#\n# The final time is set to t = 0.20 and we output a vtk every δt = 0.01.\n# For postprocessing the simulation into a nice video set the output\n# frequency to δt = = 0.0005 which will result in a 20 seconds long video\n# at 20 fps.\n#\n# [1] Woodward, Colella: The numerical simulation of two-dimensional fluid\n#     flow with strong shocks. Journal of Computational Physics 1984, vol 54.\n#     DOI rm10.1016/0021-9991(84)90142-6\n#\n##\n\n\nsubsection A - TimeLoop\n  set basename           = mach10-dbmr\n\n  set enable output full = true\n\n  set final time         = 0.20\n  set timer granularity  = 0.01\nend\n\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n  set gamma     = 1.4\nend\n\n\nsubsection C - Discretization\n  set geometry            = wall\n\n  set mesh refinement     = 7\n\n  subsection wall\n    set height        = 1\n    set length        = 3.2\n    set wall position = 0.166667\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = shock front\n\n  set direction     = 0.8660254, -0.5\n  set position      = 0.166667, 0\n\n  subsection shock front\n    set mach number     = 10\n    set primitive state = 1.4, 0, 1\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.90\n  set cfl max               = 0.90\n  set cfl recovery strategy = none\n\n  set time stepping scheme  = erk 33\nend\n\n\nsubsection J - VTUOutput\n  set use mpi io     = true\n  set schlieren beta = 10\nend\n"
  },
  {
    "path": "prm/benchmarks/euler-mach3-cylinder-2d.prm",
    "content": "##\n#\n# Supersonic channel flow past a cylinder in 2D at Mach 3.\n#\n# The geometry is a disc of diameter 0.5 positioned at coordinates {0.6,0}\n# in a channel [0,4] x [-1,1]. On the left inflow boundary Dirichlet\n# boundary conditions and on the right outflow boundary \"do nothing\"\n# conditions are enforced. On all other boundaries (top and bottom boundary\n# of the channel and the cylinder boundary) slip boundary conditions are\n# enforced.\n#\n# The chosen refinement level 8 corresponds to around 2.36M gridpoints\n# (9.44M degrees of freedom), which requires around 25k time steps and runs\n# for about (50h / #n_cores) on a modern system.\n#\n# The final time is set to t = 5.0 and we output a vtk every δt = 0.1. For\n# postprocessing the simulation into a nice video set the output frequency\n# to δt = = 0.01 which will result in a 25 seconds long video at 20 fps.\n#\n##\n\nsubsection A - TimeLoop\n  set basename           = mach3-cylinder-2d\n\n  set enable output full = true\n\n  set final time         = 5.00\n  set timer granularity  = 0.10\nend\n\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n  set gamma     = 1.4\nend\n\n\nsubsection C - Discretization\n  set geometry            = cylinder\n\n  set mesh refinement     = 8\n\n  subsection cylinder\n    set height          = 2\n    set length          = 4\n    set object diameter = 0.5\n    set object position = 0.6\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = uniform\n\n  set direction     = 1, 0\n  set position      = 1, 0\n\n  set perturbation  = 0\n\n  subsection uniform\n    set primitive state = 1.4, 3, 1\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.90\n  set cfl max               = 0.90\n  set cfl recovery strategy = none\n\n  set time stepping scheme  = erk 33\nend\n\n\nsubsection J - VTUOutput\n  set use mpi io     = true\n  set schlieren beta = 10\nend\n"
  },
  {
    "path": "prm/benchmarks/euler-mach3-cylinder-3d.prm",
    "content": "##\n#\n# Supersonic channel flow past a cylinder in 3D at Mach 3.\n#\n# Computational results for this configuration with 1.8B gridpoints are\n# discussed in [1].\n#\n# The geometry is a disc of diameter 0.5 positioned at coordinates {0.6,0}\n# in a channel [0,4] x [-1,1] and extruded into 3d with z in [-1,1] with 4\n# layers of hexahedra. On the left inflow boundary Dirichlet boundary\n# conditions and on the right outflow boundary \"do nothing\" conditions are\n# enforced. On all other boundaries (top, bottom, front, and back\n# boundaries of the channel and the cylinder boundary) slip boundary\n# conditions are enforced.\n#\n# Warning, this is a fairly large computation!\n# The chosen refinement level 6 corresponds to around 32M gridpoints (160M\n# degrees of freedom), which requires around 6.2k time steps and runs for\n# about (600h / #n_cores) on a modern system.\n#\n# [1] Maier, Kronbichler: Efficient parallel 3D computation of the\n#     compressible Euler equations with an invariant-domain preserving\n#     second-order finite-element scheme. ACM TOPC 2021, vol 8.\n#     DOI 10.1145/3470637, https://arxiv.org/abs/2007.00094\n#\n##\n\nsubsection A - TimeLoop\n  set basename                          = mach3-cylinder-3d\n\n  set enable output full                = false\n\n  #\n  # Output reduced vtks only containing hexahedra that intersect with the\n  # cutplanes x, y, z, and x^2+y^2-0.25*0.25 (as configured down below in\n  # the \"subsection J - VTUOutput\").\n  #\n  set enable output levelsets           = true\n\n  set final time                        = 5.00\n  set timer granularity                 = 0.10\n\n  set timer output full multiplier      = 10\n  set timer output levelsets multiplier = 1\nend\n\n\nsubsection B - Equation\n  set dimension = 3\n  set equation  = euler\n  set gamma     = 1.4\nend\n\n\nsubsection C - Discretization\n  set geometry            = cylinder\n\n  set mesh refinement     = 6\n\n  subsection cylinder\n    set height          = 2\n    set length          = 4\n    set object diameter = 0.5\n    set object position = 0.6\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = uniform\n\n  set direction     = 1, 0, 0\n  set position      = 1, 0, 0\n\n  set perturbation  = 0\n\n  subsection uniform\n    set primitive state = 1.4, 3, 1\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.90\n  set cfl max               = 0.90\n  set cfl recovery strategy = none\n\n  set time stepping scheme  = erk 33\nend\n\n\nsubsection J - VTUOutput\n  set manifolds      = x, y, z, x^2+y^2-0.25*0.25\n\n  set use mpi io     = true\n  set schlieren beta = 10\nend\n"
  },
  {
    "path": "prm/benchmarks/euler-mach3-forward-facing-step.prm",
    "content": "##\n#\n# Supersonic flow past a forward facing step at Mach 3.\n#\n# The geometry is a channel with bottom left corner {0,0} and top right\n# corner {3,1} but with a step inscribed at position x = 0.6 of height 0.2:\n#\n#   [0,0.6] x [0,0.2] \\cup [0,3] x [0.2,1.0]\n#\n# On the left inflow boundary Dirichlet boundary conditions and on the\n# right outflow boundary \"do nothing\" conditions are enforced. On all other\n# boundaries (top and bottom boundary of the channel includin step) slip\n# boundary conditions are enforced.\n#\n# The chosen refinement level 3 corresponds to around 1.04M gridpoints\n# (4.16M degrees of freedom), which requires around 14k time steps and runs\n# for about (15h / #n_cores) on a modern system.\n#\n# The final time is set to t = 4.0 and we output a vtk every δt = 0.1. For\n# postprocessing the simulation into a nice video set the output frequency\n# to δt = = 0.01 which will result in a 20 seconds long video at 20 fps.\n#\n##\n\nsubsection A - TimeLoop\n  set basename           = mach3-step\n\n  set enable output full = true\n\n  set final time         = 4.00\n  set timer granularity  = 0.10\nend\n\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n  set gamma     = 1.4\nend\n\n\nsubsection C - Discretization\n  set geometry            = step\n\n  set mesh refinement     = 3\n\n  subsection step\n    set height        = 1\n    set length        = 3\n    set step height   = 0.2\n    set step position = 0.6\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = uniform\n\n  set direction     = 1, 0\n  set position      = 1, 0\n\n  set perturbation  = 0\n\n  subsection uniform\n    set primitive state = 1.4, 3, 1\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.90\n  set cfl max               = 0.90\n  set cfl recovery strategy = none\n\n  set time stepping scheme  = erk 33\nend\n\n\nsubsection J - VTUOutput\n  set use mpi io     = true\n  set schlieren beta = 10\nend\n"
  },
  {
    "path": "prm/benchmarks/euler_poisson_barotropic-diocotron_instability-mode_5.prm",
    "content": "##\n#\n# DIOCOTRON INSTABILITY BENCHMARK FOR THE BAROTROPIC EULER-POISSON SYSTEM\n#\n# The domain consists of a disk with outer radius 16. Refinement level 8\n# with discontinuous elements corresponds to around 3.15M scalar degrees of\n# freedom per component, which requires around for about (32h / #n_cores)\n# on a modern system.\n#\n#\n# TIME SCALES AND MODELING PARAMETERS\n#\n#   Diocotron frequency: w_d = w_p^2 / w_c\n#   Plasma frequency:    w_p = sqrt(alpha * rho_max)\n#   Cyclotron frequency: abs(B_z)\n#\n# where the coupling constant alpha and the magnetic field component B_z\n# are chosen such that\n#\n#   alpha =  4. * pi^2 / beta^2\n#   B_z   = -2. * pi / beta^2\n#\n# and beta is a free parameter set to beta = 1e-6.\n#\n# We choose an isothermal equation of state with a speed of sound set to 1e-12.\n#\n#\n# INITIAL CONDITION\n#\n# We choose an initial density profile with perturbation:\n#\n#   rho = rho_max - delta + delta * sin(mode * theta)\n#         inside an annulus with radii r_i = 6 and r_o = 8\n#   rho = rho_min outside the annulus\n#\n# Here, rho_max = 1, delta = 0.1, rho_min = 1e-6. Solve for the initial\n# potential and set up the velocity in the magnetic drift limit.\n#\n##\n\nsubsection A - TimeLoop\n  set basename                            = diocotron_instability-mode_5\n\n  set enable output full                  = true\n\n  set final time                          = 10.00\n  set timer granularity                   = 00.10\n  set enforce final time                  = true\nend\n\n\nsubsection B - Equation\n  set dimension                    = 2\n  set equation                     = euler poisson barotropic\n\n  set barotropic equation of state = isothermal\n\n  subsection isothermal\n    set speed of sound = 1e-12\n  end\n\n  set alpha                        = 39.4784176e12\n  set set up magnetic drift limit  = true\n\n  set electrostatic configuration  = constant\n\n  subsection constant\n    set background density   = 0\n    set magnetic field       = -6.28318531e12\n  end\nend\n\n\nsubsection C - Discretization\n  set finite element ansatz = dG Q1\n\n  set mesh refinement       = 8\n  set mesh writeout         = false\n\n  set geometry              = disk\n\n  subsection disk\n    set balanced           = true\n    set radius             = 16\n    set boundary condition = do nothing\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = function\n  set position      = 0, 0\n\n  subsection function\n    # trigger fifth mode by adding a 10% perturbation:\n    set density expression    = 6 < sqrt(x*x+y*y) && sqrt(x*x+y*y) < 8 ? 1 - 0.1 + 0.1 * sin(5 * atan2(y,x)) : 1e-6\n\n    set velocity x expression = 0\n    set velocity y expression = 0\n  end\nend\n\n\nsubsection F - HyperbolicModule\n  subsection indicator\n    set evc factor        = 0.125\n  end\n\n  subsection limiter\n    set relaxation factor = 4.\n  end\nend\n\n\nsubsection G - ParabolicModule\n  set gauss law restart strategy    = full restart\n\n  set multigrid - max iter          = 15\n  set multigrid - chebyshev range   = 8\n  set multigrid - chebyshev max eig = 2\n  set multigrid - chebyshev degree  = 3\n  set multigrid - chebyshev cg iter = 10\n  set multigrid - min level         = 0\n  set tolerance                     = 1e-15\n  set tolerance linfty norm         = false\nend\n\n\nsubsection H - TimeIntegrator\n  set time stepping scheme     = strang erk 33 cn\n\n  set cfl min                  = 0.5\n  set cfl max                  = 0.5\n  set cfl recovery strategy    = cruise control\n\n  set acceptable tau_max ratio = 2.0\n  set tau_max                  = 1.0\nend\n\n\nsubsection J - VTUOutput\n  set vtu output quantities    = rho, m_1, m_2, phi\nend\n"
  },
  {
    "path": "prm/benchmarks/navier_stokes-daru-tenaud-shocktube.prm",
    "content": "##\n#\n# Navier-Stokes Shocktube Benchmark\n#\n# See [1,2].\n#\n# The shocktube consists of a square cavity (0,1)^2 with a diaphragm at\n# {x=0.5} separating it in two parts. The fluid is initially at rest. The\n# state on the left-hand side of the diaphragm is (rho_L=120, v_L=0,\n# p_L=rho_L/gamma). The right state is (rho_R=1.2, v_R=0, p_R=rho_R/gamma).\n#\n# We use the ideal gas equation of state p = (gamma - 1) rho e with\n# gamma=1.4. The bulk viscosity is set to 0. The Prandtl number is Pr=0.73.\n#\n# The chosen refinement level 10 corresponds to around 2.10M gridpoints\n# (8.40M degrees of freedom), which requires around 5k time steps and runs\n# for about (60h / #n_cores) on a modern system.\n#\n# [1] V.~Daru and C.~Tenaud. Evaluation of TVD high resolution schemes for\n#     unsteady viscous shocked flows. Computers & Fluids, 30(1):89-113, 2001\n#\n# [2] V.~Daru and C.~Tenaud. Numerical simulation of the viscous shock tube\n#     problem by using a high resolution monotonicity-preserving scheme.\n#      Computers & Fluids, 38(3):664-676, 2009.\n#\n##\n\nsubsection A - TimeLoop\n  set basename                            = shocktube-graded\n\n  set enable compute quantities           = true\n  set enable output full                  = true\n\n  set final time                          = 1.00\n  set timer granularity                   = 0.01\n\n  set timer output full multiplier        = 100\n  set timer compute quantities multiplier = 1\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = navier stokes\n\n  set mu        = 1.0e-3\n  set lambda    = 0\n\n  # Scaled thermal conductivity c_v^{-1} kappa  =  mu * gamma / Pr\n  set kappa     = 1.91780821918e-3\nend\n\nsubsection C - Discretization\n  set geometry            = rectangular domain\n  set mesh refinement     = 10\n\n  subsection rectangular domain\n    set boundary condition bottom = no slip\n    set boundary condition left   = no slip\n    set boundary condition right  = no slip\n    set boundary condition top    = slip\n\n    set position bottom left      =  0, 0\n    set position top right        =  1, 0.5\n\n    set grading pull back         = x;(1-y)^2.\n    set grading push forward      = x;1-y^(1./2.)\n    set subdivisions x            = 2\n    set subdivisions y            = 1\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = contrast\n  set direction     = 1,   0\n  set position      = 0.5, 0\n\n  subsection contrast\n    set primitive state left  = 120, 0, 85.7142857143\n    set primitive state right = 1.2, 0, 0.857142857143\n  end\nend\n\nsubsection G - ParabolicModule\n  set tolerance                              = 1e-10\n  set tolerance linfty norm                  = false\n\n  set multigrid - chebyshev degree           = 3\n  set multigrid - chebyshev cg iter          = 0\n\n  set multigrid energy                       = true\n  set multigrid energy - chebyshev max eig   = 2.0\n  set multigrid energy - chebyshev range     = 15.0\n  set multigrid energy - max iter            = 15\n\n  set multigrid velocity                     = true\n  set multigrid velocity - chebyshev max eig = 2.0\n  set multigrid velocity - chebyshev range   = 8.0\n  set multigrid velocity - max iter          = 12\nend\n\nsubsection H - TimeIntegrator\n  set cfl min                = 0.80\n  set cfl max                = 0.80\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang erk 33 cn\nend\n\nsubsection J - VTUOutput\n  set schlieren beta = 10\n  set use mpi io     = true\nend\n\n\nsubsection K - Quantities\n  set boundary manifolds = lower_boundary : y : time_averaged space_averaged\n  set interior manifolds =\nend\n"
  },
  {
    "path": "prm/benchmarks/scalar_conservation-kpp.prm",
    "content": "##\n#\n# A 2D scalar conservation equation with the kpp flux f(u)={sin(u),cos(u)}\n# as poprosed by Kurganov, Petrova, Popov [1]\n#\n#\n# The chosen refinement level 10 corresponds to around 1.05M gridpoints\n# (same number of degrees of freedom), which requires around 320 time steps\n# and runs for about (12min / #n_cores) on a modern system.\n#\n# [1] Kurganov, Petrova, Popov: Adaptive Semidiscrete Central-Upwind\n#     Schemes for Nonconvex Hyperbolic Conservation Laws. SIAM Journal on\n#     Scientific Computing 2007, vol. 29. DOI 10.1137/040614189.\n#\n##\n\n\nsubsection A - TimeLoop\n  set basename                        = kpp\n\n  set enable output full              = true\n\n  set final time                      = 1.00\n  set timer granularity               = 1.00\nend\n\n\nsubsection B - Equation\n  set dimension                       = 2\n  set equation                        = scalar conservation\n  set flux                            = kpp\nend\n\n\nsubsection C - Discretization\n  set geometry            = rectangular domain\n\n  set mesh refinement     = 10\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -2, -2.5\n    set position top right        =  2,  1.5\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = function\n  set direction     = 1, 0\n  set position      = 0, 0\n\n  subsection function\n    set expression = 0.78539816339 * if(x*x + y*y < 1, 14, 1)\n  end\nend\n\n\nsubsection F - HyperbolicModule\n  subsection riemann solver\n    set use averaged entropy = true\n    set use greedy wavespeed = true\n    set random entropies = 0\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl max               = 0.9\n  set cfl min               = 0.9\n  set cfl recovery strategy = none\n\n  set time stepping scheme  = erk 33\nend\n\n\nsubsection J - VTUOutput\n  set schlieren quantities =\n  set use mpi io           = true\nend\n"
  },
  {
    "path": "prm/benchmarks/shallow_water-G3-S2-experiment.prm",
    "content": "##\n#\n# \"G2-S2\" experiment reproduced from [1]\n#\n# The geometry is the rectangle [0, 6.0078m] x [-0.12,0.12m] which are the \n# direct measurements of the wave flume from the experiment. \n# On the left, \"dirichlet moment\" inflow conditions are encforced\n# and on the right outflow boundary, we enforce \"dynamic\" conditions.\n# On all other boundaries (top and bottom boundary of the flume) slip boundary \n# conditions are enforced.\n#\n# The chosen refinement level 3 corresponds to around 928,137 degrees of \n# freedom, which requires around 39k time steps and runs\n# for about 0.5h with 128 ranks / 2 threads on a modern system.\n#\n# The final time is set to t = 50.0 and we output a vtk every δt = 10s. For\n# postprocessing the simulation into a nice video set the output frequency\n# to δt = = 0.1.\n#\n# [1] Martínez-Aranda, S., et al. \"Towards transient experimental water \n# surfaces: A new benchmark dataset for 2D shallow water solvers.\" \n# Advances in water resources 121 (2018): 130-149.\n#\n##\n\nsubsection A - TimeLoop\n  set basename                            = output-G2-S2\n\n  set enable output full                  = true\n  set enable compute quantities           = true\n\n  set final time                          = 50\n  set timer granularity                   = 0.1\n\n  set timer output full multiplier        = 100 # Change to 1 for nice movie\n  set timer compute quantities multiplier = 100\nend\n\nsubsection B - Equation\n  set dimension                    = 2\n  set equation                     = shallow water\n\n  set reference water depth        = 1\n  set dry state relaxation large   = 1e4\n  set dry state relaxation small   = 1e2\n\n  set gravity                      = 9.81\n  set manning friction coefficient = 0.01\nend\n\nsubsection C - Discretization\n  set geometry            = rectangular domain\n  # refinement 0 -  15050 DOFs\n  # refinement 1 -  58947 DOFs\n  # refinement 2 - 233285 DOFs\n  # refinement 3 - 928137 DOFs\n  set mesh refinement     = 3\n\n  # Used for steady inflow configurations\n  subsection rectangular domain\n    set boundary condition bottom = slip\n    set boundary condition left   = dirichlet momentum\n    set boundary condition right  = dynamic\n    set boundary condition top    = slip\n    set position bottom left      = 0,      -0.12\n    set position top right        = 6.0078, 0.12\n    set subdivisions x            = 601\n    set subdivisions y            = 24\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = transient experiments\n  set direction     = 1, 0\n  set position      = 0, 0\n\n  subsection transient experiments \n  # Need to divide Q by .24m which is width of chanel and convert h->s\n  # G2 test cases -- S.1, Q = 5.05 m^3 / h -> q = 0.00585 m^2 / s\n  #               -- S.2, Q = 9.01 m^3 / h -> q = 0.0104 m^2 / s\n  #               -- D.1, h0 = 0.13m\n  set experimental configuration  = G2\n    # note that the depth value does not matter\n    set flow state left           = 0.1, 0.0104  \n    set flow state right          = 0,     0\n  end \nend\n\nsubsection F - HyperbolicModule\n  subsection indicator\n    set evc factor = 1\n  end\n\n  subsection limiter\n    set iterations               = 2\n    set newton max iterations    = 2\n    set newton tolerance         = 1e-10\n    set relaxation factor        = 2\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl max               = 0.9\n  set cfl min               = 0.45\n  set cfl recovery strategy = bang bang control\n  set time stepping scheme  = erk 43\nend\n\nsubsection J - VTUOutput\n  set manifolds                  = \n  set schlieren beta             = 10\n  set schlieren quantities       = h\n  set schlieren recompute bounds = true\n  set use mpi io                 = true\n  set vtu output quantities      = h, m_1, m_2, bathymetry, alpha, v_1\nend\n\nsubsection K - Quantities\n  set clear statistics on writeout = true\n  set interior manifolds = lineout-x : x - 2.40 : instantaneous, \\\n                           lineout-y : y - 0.   : instantaneous  \nend\n"
  },
  {
    "path": "prm/todo/ideal-blast.prm",
    "content": "subsection A - TimeLoop\n  set basename           = wc-blast-ideal\n\n  set enable output full = true\n\n  set final time         = 0.038\n  set timer granularity  = 0.001\nend\n\nsubsection B - Equation\n  set equation                 = euler aeos\n  set equation of state        = polytropic gas\n\n  subsection polytropic gas\n    set gamma = 1.4\n  end\n\n  set interpolation co-volume  = 0\n  set reference density        = 1\n  set vacuum state relaxation  = 10000\n  set compute expensive bounds = false\nend\n\nsubsection C - Discretization\n  set geometry            = rectangular domain\n  set mesh distortion     = 0\n  set mesh refinement     = 10\n\n  subsection rectangular domain\n    set boundary condition left  = slip\n    set boundary condition right = slip\n    set grading pull back        = x\n    set grading push forward     = x\n    set position bottom left     = 0\n    set position top right       = 1\n    set subdivisions x           = 1\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = TwoContrast\n  set direction     = 1\n  set perturbation  = 0\n  set position      = 0  # 0 is the origin\n\n  subsection TwoContrast\n    set left region length     = 0.1\n    set middle region length   = 0.8\n    # (rho, u, e)\n    set primitive state left   = 1, 0, 2500\n    set primitive state middle = 1, 0, 0.025\n    set primitive state right  = 1, 0, 250\n  end\nend\n\nsubsection F - HyperbolicModule\n  set cfl with boundary dofs        = false\n  set limiter iterations            = 2\n  set limiter newton max iterations = 2\n  set limiter newton tolerance      = 1e-10\n  set limiter relaxation factor     = 1\nend\n\nsubsection H - TimeIntegrator\n  set cfl max               = 0.9\n  set cfl min               = 0.45\n  set cfl recovery strategy = bang bang control\n  set time stepping scheme  = erk 33\nend\n\nsubsection J - VTUOutput\n  set manifolds                  = \n  set schlieren beta             = 10\n  set schlieren quantities       = \n  set schlieren recompute bounds = true\n  set use mpi io                 = true\n  set vtu output quantities      = rho, m, E, p\nend\n\n\n"
  },
  {
    "path": "prm/verification/euler-isentropic_vortex-erk22.baseline",
    "content": "#dofs =  4225, double, ERK(2,2,1), baseline, Harten entropy\nt     = 2.00318\nLinf  = 0.00544182\nL1    = 0.000397499\nL2    = 0.000933162\n\n#dofs = 16641, double, ERK(2,2,1), baseline, Harten entropy\nt     = 2.0025\nLinf  = 0.000645336\nL1    = 4.92351e-05\nL2    = 0.000115145\n\n#dofs = 66049, double, ERK(2,2,1), baseline, Harten entropy\nt     = 2.00062\nLinf  = 8.17578e-05\nL1    = 6.53854e-06\nL2    = 1.47069e-05\n\n#dofs = 263169, double, ERK(2,2,1), baseline, Harten entropy\nt     = 2.00081\nLinf  = 1.05043e-05\nL1    = 8.91935e-07\nL2    = 1.91348e-06\n\n#dofs = 1050625, double, ERK(2,2,1), baseline, Harten entropy\nt     = 2.00001\nLinf  = 1.43653e-06\nL1    = 1.3147e-07\nL2    = 2.64076e-07\n"
  },
  {
    "path": "prm/verification/euler-isentropic_vortex-erk22.prm",
    "content": "##\n#\n# Euler (optimized polytropic gas EOS) benchmark:\n#\n# The isentropic vortex is an analytic solution of the compressible Euler\n# equations (with polytropic gas equation of state). We compute the vortex\n# on the square [-5, 5]^2 initially centered at (-1,-1) with a strength of\n# beta=5, and moving in diagonal (1,1) direction with mach number 1. At\n# final time t=2 the vortex is located at (1, 1). We report the final,\n# normalized L1, L2, L\\infty error norms summed up over all components.\n#\n# This configuration uses an explicit ERK(2, 2, 1) timestepping.\n# Expected results are reported in euler-isentropic_vortex-erk22.baseline\n#\n##\n\nsubsection A - TimeLoop\n  set basename             = isentropic_vortex-erk22\n\n  set enable compute error = true\n  set error normalize      = true\n  set error quantities     = rho, m_1, m_2, E\n\n  set enable output full   = true\n\n  set final time           = 2.0\n  set timer granularity    = 2.0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n\n  set gamma     = 1.4\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 7\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.2\n  set cfl max               = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 22\nend\n"
  },
  {
    "path": "prm/verification/euler-isentropic_vortex-erk33.baseline",
    "content": "#dofs =  4225, double, ERK(3,3,1), baseline, Harten entropy\nt     = 2.00693\nLinf  = 0.00546534\nL1    = 0.000401734\nL2    = 0.000944194\n\n#dofs = 16641, double, ERK(3,3,1), baseline, Harten entropy\nt     = 2.00063\nLinf  = 0.000640962\nL1    = 4.95127e-05\nL2    = 0.000116091\n\n#dofs = 66049, double, ERK(3,3,1), baseline, Harten entropy\nt     = 2.00062\nLinf  = 7.94203e-05\nL1    = 6.54002e-06\nL2    = 1.47543e-05\n\n#dofs = 263169, double, ERK(3,3,1), baseline, Harten entropy\nt     = 2.00081\nLinf  = 9.75774e-06\nL1    = 8.7572e-07\nL2    = 1.88157e-06\n\n#dofs = 1050625, double, ERK(3,3,1), baseline, Harten entropy\nt     = 2.00024\nLinf  = 1.20076e-06\nL1    = 1.22744e-07\nL2    = 2.43871e-07\n\n\n\n#dofs =  4225, float, ERK(3,3,1), baseline, Harten entropy\nt     = 2.00693\nLinf  = 0.0054715\nL1    = 0.000403722\nL2    = 0.000944413\n\n#dofs = 16641, float, ERK(3,3,1), baseline, Harten entropy\nt     = 2.00063\nLinf  = 0.000657314\nL1    = 5.98743e-05\nL2    = 0.000118905\n\n#dofs = 66049, float, ERK(3,3,1), baseline, Harten entropy\nt     = 2.00061\nLinf  = 0.000163494\nL1    = 2.87595e-05\nL2    = 3.67441e-05\n"
  },
  {
    "path": "prm/verification/euler-isentropic_vortex-erk33.prm",
    "content": "##\n#\n# Euler (optimized polytropic gas EOS) benchmark:\n#\n# The isentropic vortex is an analytic solution of the compressible Euler\n# equations (with polytropic gas equation of state). We compute the vortex\n# on the square [-5, 5]^2 initially centered at (-1,-1) with a strength of\n# beta=5, and moving in diagonal (1,1) direction with mach number 1. At\n# final time t=2 the vortex is located at (1, 1). We report the final,\n# normalized L1, L2, L\\infty error norms summed up over all components.\n#\n# This configuration uses an explicit ERK(3, 3, 1) timestepping.\n# Expected results are reported in euler-isentropic_vortex-erk33.baseline\n#\n##\n\nsubsection A - TimeLoop\n  set basename             = isentropic_vortex-erk33\n\n  set enable compute error = true\n  set error normalize      = true\n  set error quantities     = rho, m_1, m_2, E\n\n  set enable output full   = true\n\n  set final time           = 2.0\n  set timer granularity    = 2.0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n\n  set gamma     = 1.4\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 7\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.2\n  set cfl max               = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "prm/verification/euler-isentropic_vortex-ssprk33.baseline",
    "content": "#dofs =  4225, double, SSPRK(3,3,1/3), baseline, Harten entropy\nt     = 2.00318619735729\nLinf  = 0.005451477418579575\nL1    = 0.0004007415406445266\nL2    = 0.0009421608090400022\n\n#dofs = 16641, double, SSPRK(3,3,1/3), baseline, Harten entropy\nt     = 2.000630303188456\nLinf  = 0.0006405629184534286\nL1    = 4.950658926367519e-05\nL2    = 0.0001160759856448879\n\n#dofs = 66049, double, SSPRK(3,3,1/3), baseline, Harten entropy\nt     = 2.000617885965\nLinf  = 7.939946199946041e-05\nL1    = 6.539414482553086e-06\nL2    = 1.475285020323413e-05\n\n#dofs = 263169, double, SSPRK(3,3,1/3), baseline, Harten entropy\nt     = 2.00034548436163\nLinf  = 9.752575958911255e-06\nL1    = 8.754501695918193e-07\nL2    = 1.880973220361095e-06\n\n#dofs = 1050625, double, SSPRK(3,3,1/3), baseline, Harten entropy\nt     = 2.00001\nLinf  = 1.19459e-06\nL1    = 1.0651e-07\nL2    = 2.35858e-07\n"
  },
  {
    "path": "prm/verification/euler-isentropic_vortex-ssprk33.prm",
    "content": "##\n#\n# Euler (optimized polytropic gas EOS) benchmark:\n#\n# The isentropic vortex is an analytic solution of the compressible Euler\n# equations (with polytropic gas equation of state). We compute the vortex\n# on the square [-5, 5]^2 initially centered at (-1,-1) with a strength of\n# beta=5, and moving in diagonal (1,1) direction with mach number 1. At\n# final time t=2 the vortex is located at (1, 1). We report the final,\n# normalized L1, L2, L\\infty error norms summed up over all components.\n#\n# This configuration uses an explicit ERK(2, 2, 1) timestepping.\n# Expected results are reported in euler-isentropic_vortex-ssprk33.baseline\n#\n##\n\nsubsection A - TimeLoop\n  set basename             = isentropic_vortex-ssprk33\n\n  set enable compute error = true\n  set error normalize      = true\n  set error quantities     = rho, m_1, m_2, E\n\n  set enable output full   = true\n\n  set final time           = 2.0\n  set timer granularity    = 2.0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n\n  set gamma     = 1.4\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 7\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.2\n  set cfl max               = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = ssprk 33\nend\n"
  },
  {
    "path": "prm/verification/euler-leblanc-erk33.baseline",
    "content": "#dofs = 401, double, ERK(3,3,1), baseline, Harten entropy\nt     = 0.6668241058025716\nLinf  = 0.2713652236947977\nL1    = 0.03812908254257573\nL2    = 0.06786373158086266\n\n#dofs = 801, double, ERK(3,3,1), baseline, Harten entropy\nt     = 0.6667411276802717\nLinf  = 0.23685341534153\nL1    = 0.02112958522884788\nL2    = 0.0419720558375091\n\n#dofs = 1601, double, ERK(3,3,1), baseline, Harten entropy\nt     = 0.66668049721543\nLinf  = 0.2153587144075435\nL1    = 0.01126070081400691\nL2    = 0.03289718038556945\n\n#dofs = 3201, double, ERK(3,3,1), baseline, Harten entropy\nt     = 0.6666831955865451\nLinf  = 0.2733985565668812\nL1    = 0.005692176332260442\nL2    = 0.02282297775333933\n\n#dofs = 6400, double, ERK(3,3,1), baseline, Harten entropy\nt     = 0.6666859156341352\nLinf  = 0.2666225151178555\nL1    = 0.003669699871756991\nL2    = 0.01746098096003046\n"
  },
  {
    "path": "prm/verification/euler-leblanc-erk33.prm",
    "content": "##\n#\n# Euler (optimized polytropic gas EOS) benchmark:\n#\n# The Leblanc shocktube is an analytic solution of the compressible Euler\n# equations (with polytropic gas equation of state). We report the final,\n# normalized L1, L2, L\\infty error norms summed up the rho, m_1, and E\n# components.\n#\n# This configuration uses an explicit ERK(3, 3, 1) timestepping.\n# Expected results are reported in euler-leblanc-erk33.baseline\n#\n# The expected optimal rate of this test case is 1 in the L1 norm.\n#\n##\n\nsubsection A - TimeLoop\n  set basename             = leblanc-erk33\n\n  set enable compute error = true\n  set error normalize      = true\n  set error quantities     = rho, m, E\n\n  set enable output full   = true\n\n  set final time           = 0.66666666666667\n  set timer granularity    = 0.66666666666667\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = euler\n\n  set gamma     = 1.66666666666667\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = 0\n    set position top right        = 1\n    set subdivisions x            = 25\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = leblanc\n\n  set direction     = 1\n  # for cG put the discontinuity right in the center of a cell\n  set position      = 0.326732673267\nend\n\n\nsubsection F - HyperbolicModule\n  subsection limiter\n    set iterations            = 2\n    set newton max iterations = 2\n    set newton tolerance      = 1e-10\n    set relaxation factor     = 4\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.10\n  set cfl max               = 0.10\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n\n\nsubsection J - VTUOutput\n  set manifolds      =\n  set use mpi io     = true\n  set schlieren beta = 10\nend\n"
  },
  {
    "path": "prm/verification/euler-rarefaction_erk33.baseline",
    "content": "#dofs = 401, double, ERK(3,3,1), baseline, Harten entropy\nt     = 0.3057266771434102\nLinf  = 0.004713340356388092\nL1    = 0.0001602606061415145\nL2    = 0.0004493802441606568\n\n#dofs = 801, double, ERK(3,3,1), baseline, Harten entropy\nt     = 0.3057398268233247\nLinf  = 0.002494086169788512\nL1    = 5.251921991282682e-05\nL2    = 0.0001856046036735253\n\n#dofs = 1601, double, ERK(3,3,1), baseline, Harten entropy\nt     = 0.305747728210163\nLinf  = 0.001312810233412834\nL1    = 1.643470771031956e-05\nL2    = 7.710730352644506e-05\n\n#dofs = 3201, double, ERK(3,3,1), baseline, Harten entropy\nt     = 0.3056453950989957\nLinf  = 0.0005601043188784826\nL1    = 4.827508819264399e-06\nL2    = 3.100678689925009e-05\n\n#dofs = 6401, double, ERK(3,3,1), baseline, Harten entropy\nt     = 0.3055946913431965\nLinf  = 0.0003353038273075571\nL1    = 1.442796191389103e-06\nL2    = 1.318667153523106e-05\n"
  },
  {
    "path": "prm/verification/euler-rarefaction_erk33.prm",
    "content": "##\n#\n# Euler (optimized polytropic gas EOS) benchmark:\n#\n# The rarefaction wave is an analytic solution of the compressible Euler\n# equations (with polytropic gas equation of state). See Section 5.3 of\n# @cite GuermondEtAl2018 and Section 4.4 in @cite Toro2009. The problem is\n# challenging to solve for methods enforcing a minimum priniciple on the\n# specific entropy. With strict limiting the convergence rate reduces to 1.\n# The best-approximation error for the problem in the L1-norm is 2.\n#\n# We report the final, normalized L1, L2, L\\infty error norms summed up\n# over the rho, m_1, and E components.\n#\n# This configuration uses an explicit ERK(3, 3, 1) timestepping.\n# Expected results are reported in euler-rarefaction-erk33.baseline\n#\n# For cG Q1 elements we expect a convergence rate of around ~1.75 in the L1\n# norm for our choice of limiting and bounds relaxation.\n#\n##\n\nsubsection A - TimeLoop\n  set basename             = rarefaction-erk33\n\n  set enable compute error = true\n  set error normalize      = true\n  set error quantities     = rho, m, E\n\n  set enable output full   = true\n\n  set final time           = 0.30558\n  set timer granularity    = 0.30558\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = euler\n\n  set gamma     = 1.4\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = 0\n    set position top right        = 1\n    set subdivisions x            = 25\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = rarefaction\n\n  set direction     = 1\n  set position      = 0.2\nend\n\n\nsubsection F - HyperbolicModule\n  subsection limiter\n    set iterations            = 2\n    set newton max iterations = 2\n    set newton tolerance      = 1e-10\n    set relaxation factor     = 8\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.50\n  set cfl max               = 0.50\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n\n\nsubsection J - VTUOutput\n  set manifolds      =\n  set use mpi io     = true\n  set schlieren beta = 10\nend\n"
  },
  {
    "path": "prm/verification/euler-shock_front_erk33.baseline",
    "content": "#dofs = 401, double, ERK(3,3,1), baseline, Harten entropy\nt     = 0.2500062136160995\nLinf  = 0.5936493048718199\nL1    = 0.02225992640021175\nL2    = 0.04063635152029894\n\n#dofs = 801, double, ERK(3,3,1), baseline, Harten entropy\nt     = 0.2500108020218645\nLinf  = 0.6671759089177104\nL1    = 0.008583758936884027\nL2    = 0.02569630606862452\n\n#dofs = 1601, double, ERK(3,3,1), baseline, Harten entropy\nt     = 0.2500324445924851\nLinf  = 0.7891912524876312\nL1    = 0.003365082670890948\nL2    = 0.01955143703146916\n\n#dofs = 3201, double, ERK(3,3,1), baseline, Harten entropy\nt     = 0.2500081921530498\nLinf  = 0.7230936442745308\nL1    = 0.001247650992234553\nL2    = 0.01283891157244705\n\n#dofs = 6401, double, ERK(3,3,1), baseline, Harten entropy\nt     = 0.2500043933307497\nLinf  = 0.7181873261051833\nL1    = 0.0004725018521826975\nL2    = 0.008995877679502875\n"
  },
  {
    "path": "prm/verification/euler-shock_front_erk33.prm",
    "content": "##\n#\n# Euler (optimized polytropic gas EOS) benchmark:\n#\n# The S1/S3 shock front is discontinuous, analytic solution of the\n# compressible Euler equations (with polytropic gas equation of state).\n#\n# We report the final, normalized L1, L2, L\\infty error norms summed up\n# over the rho, m_1, and E components.\n#\n# This configuration uses an explicit ERK(3, 3, 1) timestepping.\n# Expected results are reported in euler-shock_front-erk33.baseline\n#\n##\n\nsubsection A - TimeLoop\n  set basename             = shock_front-erk33\n\n  set enable compute error = true\n  set error normalize      = true\n  set error quantities     = rho, m, E\n\n  set enable output full   = true\n\n  set final time           = 0.25\n  set timer granularity    = 0.25\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = euler\n\n  set gamma     = 1.4\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = 0\n    set position top right        = 1\n    set subdivisions x            = 25\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = shock front\n\n  set direction     = 1\n  set position      = 0.25\nend\n\n\nsubsection F - HyperbolicModule\n  subsection limiter\n    set iterations            = 2\n    set newton max iterations = 2\n    set newton tolerance      = 1e-10\n    set relaxation factor     = 8\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.10\n  set cfl max               = 0.10\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n\n\nsubsection J - VTUOutput\n  set manifolds      =\n  set use mpi io     = true\n  set schlieren beta = 10\nend\n"
  },
  {
    "path": "prm/verification/euler-smooth_wave-erk33.baseline",
    "content": "#dofs = 401, double, ERK(3,3,1), baseline, Harten entropy\nt     = 0.6002747494635589\nLinf  = 0.000946215450325123\nL1    = 8.495622969362369e-05\nL2    = 0.0002064883626207843\n\n#dofs = 801, double, ERK(3,3,1), baseline, Harten entropy\nt     = 0.6000529741376704\nLinf  = 0.0001872671404596658\nL1    = 1.057135864508406e-05\nL2    = 2.894102155048331e-05\n\n#dofs = 1601, double, ERK(3,3,1), baseline, Harten entropy\nt     = 0.6000594019362628\nLinf  = 3.621829539670038e-05\nL1    = 1.291602520873936e-06\nL2    = 4.066149880662045e-06\n\n#dofs = 3201, double, ERK(3,3,1), baseline, Harten entropy\nt     = 0.6000605986103484\nLinf  = 6.703443833525207e-06\nL1    = 1.552287941282637e-07\nL2    = 5.730390940018639e-07\n\n#dofs = 6401, double, ERK(3,3,1), baseline, Harten entropy\nt     = 0.6000286231342374\nLinf  = 1.263429819043142e-06\nL1    = 1.858274691583733e-08\nL2    = 8.104689582888686e-08\n"
  },
  {
    "path": "prm/verification/euler-smooth_wave-erk33.prm",
    "content": "##\n#\n# Euler (optimized polytropic gas EOS) benchmark:\n#\n# The Smooth traveling wave is an analytic solution of the compressible\n# Euler equations (with polytropic gas equation of state). We report the\n# final, normalized L1, L2, L\\infty error norms summed up over the rho,\n# m_1, and E components.\n#\n# This configuration uses an explicit ERK(3, 3, 1) timestepping.\n# Expected results are reported in euler-smooth_wave-erk33.baseline\n#\n##\n\nsubsection A - TimeLoop\n  set basename             = smooth_wave-erk33\n\n  set enable compute error = true\n  set error normalize      = true\n  set error quantities     = rho, m, E\n\n  set enable output full   = true\n\n  set final time           = 0.60\n  set timer granularity    = 0.60\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = euler\n\n  set gamma     = 1.4\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = 0\n    set position top right        = 1\n    set subdivisions x            = 25\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = smooth wave\n\n  set direction     = 1\n  set position      = 0.1\nend\n\n\nsubsection F - HyperbolicModule\n  subsection limiter\n    set iterations            = 2\n    set newton max iterations = 2\n    set newton tolerance      = 1e-10\n    set relaxation factor     = 1\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.30\n  set cfl max               = 0.30\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n\n\nsubsection J - VTUOutput\n  set manifolds      =\n  set use mpi io     = true\n  set schlieren beta = 10\nend\n"
  },
  {
    "path": "prm/verification/euler_poisson_barotropic-isentropic_vortex-strang_erk33_cn-static.baseline",
    "content": "  #dofs     L1 error           L2 error          Linf error\n-------  -----------  ----  -----------  ----  ------------  ----\n     81  0.100629           0.176741            0.769062\n    289  0.0216484    2.22  0.045545     1.96   0.213164     1.85\n   1089  0.00316082   2.78  0.00767935   2.57   0.0434121    2.3\n   4225  0.00036237   3.12  0.000841402  3.19   0.0040894    3.41\n  16641  4.4198e-05   3.04  0.000103596  3.02   0.000518535  2.98\n  66049  5.7857e-06   2.93  1.31051e-05  2.98   6.65622e-05  2.96\n 263169  7.68245e-07  2.91  1.66495e-06  2.98   8.22994e-06  3.02\n1050625  1.06605e-07  2.85  2.15006e-07  2.95   1.01534e-06  3.02\n\n#dofs = 1089\nt     = 2.037025077485755\nLinf  = 0.04341206436522653\nL1    = 0.003160824281583258\nL2    = 0.007679350924034741\n\n#dofs = 4225\nt     = 2.001596262139736\nLinf  = 0.004089400289349965\nL1    = 0.0003623697105562372\nL2    = 0.0008414024532127028\n\n#dofs = 16641\nt     = 2.005478361294224\nLinf  = 0.0005185348832242804\nL1    = 4.419804820133751e-05\nL2    = 0.0001035958515038214\n\n#dofs = 66049\nt     = 2.005478363233932\nLinf  = 6.656222887619342e-05\nL1    = 5.785695646217787e-06\nL2    = 1.310510481601859e-05\n\n#dofs = 263169\nt     = 2.002692977439805\nLinf  = 8.229937462860163e-06\nL1    = 7.682447254973924e-07\nL2    = 1.66495146127964e-06\n\n#dofs = 1050625\nt     = 2.001300284512737\nLinf  = 1.01533645335673e-06\nL1    = 1.066047578910122e-07\nL2    = 2.150055866633999e-07\n"
  },
  {
    "path": "prm/verification/euler_poisson_barotropic-isentropic_vortex-strang_erk33_cn-static.prm",
    "content": "##\n#\n# Euler Poisson (isentropic EOS) benchmark:\n#\n# The isentropic vortex is an analytic solution of the compressible Euler\n# equations (with polytropic gas equation of state). We compute the vortex\n# on the square [-5, 5]^2 initially centered at (-1,-1) with a strength of\n# beta=5, and moving in diagonal (1,1) direction with mach number 1. At\n# final time t=2 the vortex is located at (1, 1). We report the final,\n# normalized L1, L2, L\\infty error norms summed up over all components.\n#\n# We add a time-dependent \"neutralizing\" background density that balances\n# the (charge) density of the isentropic vortex so that the resulting\n# (analytic) potential is identically zero.\n#\n# This variant uses the \"static full restart\" update strategy and a cG\n# finite element ansatz.\n#\n# This configuration uses an explicit/implicit Strang split with ERK(3, 3,\n# 1) and Crank Nicolson. Expected results are reported in\n# euler_poisson-isentropic_vortex-strang_erk33_cn-static.baseline\n#\n##\n\nsubsection A - TimeLoop\n  set basename             = isentropic_vortex-strang_erk_33_cn\n\n  set enable compute error = true\n  set error normalize      = true\n  set error quantities     = rho, m_1, m_2\n\n  set enable output full   = true\n\n  set final time           = 2.0\n  set timer granularity    = 2.0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler poisson barotropic\n\n  set barotropic equation of state = isentropic\n  set alpha                        = 1\n  set set up magnetic drift limit  = false\n  set electrostatic configuration  = function\n\n  subsection isentropic\n    set k     = 1.\n    set gamma = 1.4\n  end\n\n  subsection function\n    set time dependent           = true\n    set magnetic field z         = 0.\n\n    ##\n    # rho_b     = -T^(1 / (gamma - 1))\n    # T         = 1 - (gamma - 1) / (2 * gamma) * f^2\n    # f         = beta / (2 * pi) * exp(0.5 * (1 - |r|^2))\n    # r         = (x,y) - (x_0,y_0) - Mt\n    # x_0 = y_0 = (-1, -1)\n    # M         = ( 1,  1)\n    # gamma     = 1.4\n    # beta      = 5\n    ##\n\n    set background density = -pow(1.-(1.4-1.)/(2 * 1.4) * pow(5. / (2. * 3.1415926535897932) * exp(0.5 * (1. - pow(x + 1. - 0.707106781188 * t, 2.) - pow(y + 1. - 0.707106781188 * t, 2.))), 2.), 1. / (1.4 - 1.))\n  end\nend\n\nsubsection C - Discretization\n  set finite element ansatz = cG Q1\n\n  set geometry        = rectangular domain\n  set mesh refinement = 7\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection G - ParabolicModule\n  set gauss law restart strategy    = static full restart\n\n  set multigrid - max iter          = 15\n  set multigrid - chebyshev range   = 8\n  set multigrid - chebyshev max eig = 2\n  set multigrid - chebyshev degree  = 3\n  set multigrid - chebyshev cg iter = 0\n  set multigrid - min level         = 0\n  set tolerance                     = 1e-15\n  set tolerance linfty norm         = false\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set gamma       = 1.4\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.2\n  set cfl max               = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang erk 33 cn\nend\n\nsubsection J - VTUOutput\n  set vtu output quantities      = rho, m_1, m_2, phi\nend\n"
  },
  {
    "path": "prm/verification/euler_poisson_barotropic-isentropic_vortex-strang_erk33_cn.baseline",
    "content": "  #dofs     L1 error           L2 error          Linf error\n-------  -----------  ----  -----------  ----  ------------  ----\n     81  0.102581           0.175879            0.755665\n    289  0.0233674    2.13  0.0460387    1.93   0.203533     1.89\n   1089  0.00413464   2.5   0.00827324   2.48   0.0421239    2.27\n   4225  0.000695038  2.57  0.00117335   2.82   0.00533886   2.98\n  16641  0.00014723   2.24  0.000228234  2.36   0.000911327  2.55\n  66049  3.49287e-05  2.08  5.23337e-05  2.12   0.00019431   2.23\n 263169  8.57693e-06  2.03  1.27963e-05  2.03   4.63492e-05  2.07\n\n#dofs = 1089\nt     = 2.037340274178982\nLinf  = 0.04212394192102632\nL1    = 0.004134637936244537\nL2    = 0.008273238858040582\n\n#dofs = 4225\nt     = 2.001660696590581\nLinf  = 0.005338856826091217\nL1    = 0.0006950378591315189\nL2    = 0.001173353381677049\n\n#dofs = 16641\nt     = 2.00547821279349\nLinf  = 0.0009113273742106492\nL1    = 0.0001472298053709277\nL2    = 0.0002282338594175808\n\n#dofs = 66049\nt     = 2.005478326596105\nLinf  = 0.0001943100825617442\nL1    = 3.492869359511232e-05\nL2    = 5.233367815315656e-05\n\n#dofs = 263169\nt     = 2.002692968395132\nLinf  = 4.634918716140793e-05\nL1    = 8.576931703791374e-06\nL2    = 1.279626337824636e-05\n"
  },
  {
    "path": "prm/verification/euler_poisson_barotropic-isentropic_vortex-strang_erk33_cn.prm",
    "content": "##\n#\n# Euler Poisson (isentropic EOS) benchmark:\n#\n# The isentropic vortex is an analytic solution of the compressible Euler\n# equations (with polytropic gas equation of state). We compute the vortex\n# on the square [-5, 5]^2 initially centered at (-1,-1) with a strength of\n# beta=5, and moving in diagonal (1,1) direction with mach number 1. At\n# final time t=2 the vortex is located at (1, 1). We report the final,\n# normalized L1, L2, L\\infty error norms summed up over all components.\n#\n# We add a time-dependent \"neutralizing\" background density that balances\n# the (charge) density of the isentropic vortex so that the resulting\n# (analytic) potential is identically zero.\n#\n# This variant uses the \"no restart\" update strategy and a dG finite\n# element ansatz.\n#\n# This configuration uses an explicit/implicit Strang split with ERK(3, 3,\n# 1) and Crank Nicolson. Expected results are reported in\n# euler_poisson-isentropic_vortex-strang_erk33_cn.baseline\n#\n##\n\nsubsection A - TimeLoop\n  set basename             = isentropic_vortex-strang_erk_33_cn\n\n  set enable compute error = true\n  set error normalize      = true\n  set error quantities     = rho, m_1, m_2\n\n  set enable output full   = true\n\n  set final time           = 2.0\n  set timer granularity    = 2.0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler poisson barotropic\n\n  set barotropic equation of state = isentropic\n  set alpha                        = 1\n  set set up magnetic drift limit  = false\n  set electrostatic configuration  = function\n\n  subsection isentropic\n    set k     = 1.\n    set gamma = 1.4\n  end\n\n  subsection function\n    set time dependent           = true\n    set magnetic field z         = 0.\n\n    ##\n    # rho_b     = -T^(1 / (gamma - 1))\n    # T         = 1 - (gamma - 1) / (2 * gamma) * f^2\n    # f         = beta / (2 * pi) * exp(0.5 * (1 - |r|^2))\n    # r         = (x,y) - (x_0,y_0) - Mt\n    # x_0 = y_0 = (-1, -1)\n    # M         = ( 1,  1)\n    # gamma     = 1.4\n    # beta      = 5\n    ##\n\n    set background density = -pow(1.-(1.4-1.)/(2 * 1.4) * pow(5. / (2. * 3.1415926535897932) * exp(0.5 * (1. - pow(x + 1. - 0.707106781188 * t, 2.) - pow(y + 1. - 0.707106781188 * t, 2.))), 2.), 1. / (1.4 - 1.))\n  end\nend\n\nsubsection C - Discretization\n  set finite element ansatz = dG Q1\n\n  set geometry        = rectangular domain\n  set mesh refinement = 7\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection G - ParabolicModule\n  set gauss law restart strategy    = no restart\n\n  set multigrid - max iter          = 15\n  set multigrid - chebyshev range   = 8\n  set multigrid - chebyshev max eig = 2\n  set multigrid - chebyshev degree  = 3\n  set multigrid - chebyshev cg iter = 0\n  set multigrid - min level         = 0\n  set tolerance                     = 1e-15\n  set tolerance linfty norm         = false\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set gamma       = 1.4\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.2\n  set cfl max               = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang erk 33 cn\nend\n\nsubsection J - VTUOutput\n  set vtu output quantities      = rho, m_1, m_2, phi\nend\n"
  },
  {
    "path": "prm/verification/linear_transport-time_stepping.baseline",
    "content": "erk 11 (CFL = 0.05)\n\n  #dofs     L1 error          L2 error         Linf error\n-------  -----------  ---  -----------  ---  ------------  ----\n     33  0.00492741        0.00492842         0.00493372\n     65  0.00245758   1.0  0.00245776   1.0   0.0024573    1.01\n    129  0.00122813   1.0  0.00122813   1.0   0.00122814   1.0\n    257  0.000613876  1.0  0.000613876  1.0   0.000613877  1.0\n    513  0.000306844  1.0  0.000306844  1.0   0.000306844  1.0\n   1025  0.00015341   1.0  0.00015341   1.0   0.00015341   1.0\n   2049  7.67021e-05  1.0  7.67021e-05  1.0   7.67055e-05  1.0\n\nerk 22 (CFL = 0.2)\n\n  #dofs     L1 error           L2 error          Linf error\n-------  -----------  ----  -----------  ----  ------------  ----\n     33  0.000415733        0.000415945         0.00041654\n     65  0.00012249   1.76  0.000122492  1.76   0.000122485  1.77\n    129  3.17843e-05  1.95  3.17845e-05  1.95   3.17856e-05  1.95\n    257  8.01877e-06  1.99  8.01878e-06  1.99   8.01898e-06  1.99\n    513  2.00678e-06  2.0   2.00678e-06  2.0    2.00679e-06  2.0\n   1025  5.01977e-07  2.0   5.01977e-07  2.0    5.01977e-07  2.0\n   2049  1.25512e-07  2.0   1.25512e-07  2.0    1.25512e-07  2.0\n\nerk 33 (CFL = 0.8)\n\n  #dofs     L1 error           L2 error          Linf error\n-------  -----------  ----  -----------  ----  ------------  ----\n     33  0.0012943          0.00162824          0.00310685\n     65  0.000135965  3.25  0.000135968  3.58   0.000136075  4.51\n    129  1.70412e-05  3.0   1.7041e-05   3.0    1.70394e-05  3.0\n    257  2.13151e-06  3.0   2.13151e-06  3.0    2.13158e-06  3.0\n    513  2.66482e-07  3.0   2.66482e-07  3.0    2.66484e-07  3.0\n   1025  3.33116e-08  3.0   3.33116e-08  3.0    3.33117e-08  3.0\n   2049  4.16393e-09  3.0   4.16395e-09  3.0    4.164e-09    3.0\n\nerk 43 (CFL = 0.8)\n\n  #dofs     L1 error           L2 error          Linf error\n-------  -----------  ----  -----------  ----  ------------  ----\n     33  0.00162339         0.00218677          0.00556607\n     65  0.000135387  3.58  0.000174416  3.65   0.00046423   3.58\n    129  1.04202e-06  7.02  1.04203e-06  7.39   1.042e-06    8.8\n    257  6.39049e-08  4.03  6.39056e-08  4.03   6.39033e-08  4.03\n    513  3.99468e-09  4.0   3.99468e-09  4.0    3.99461e-09  4.0\n   1025  2.49767e-10  4.0   2.49754e-10  4.0    2.49666e-10  4.0\n   2049  1.57202e-11  3.99  1.57084e-11  3.99   1.58684e-11  3.98\n\nerk 54 (CFL = 0.8)\n\n  #dofs     L1 error           L2 error           Linf error\n-------  -----------  ----  -----------  -----  ------------  -----\n     33  0.00697339         0.00853865           0.0133337\n     65  0.00110367   2.66  0.00176184   2.28    0.00399207   1.74\n    129  0.00012141   3.18  0.000245868  2.84    0.000772719  2.37\n    257  1.2707e-07   9.9   1.2707e-07   10.92   1.27067e-07  12.57\n    513  7.94298e-09  4.0   7.94296e-09  4.0     7.943e-09    4.0\n   1025  4.93517e-10  4.01  4.93504e-10  4.01    4.93416e-10  4.01\n   2049  3.09374e-11  4.0   3.09251e-11  4.0     3.09175e-11  4.0\n\nssprk 22 (CFL = 0.8)\n\n  #dofs     L1 error           L2 error          Linf error\n-------  -----------  ----  -----------  ----  ------------  ----\n     33  0.0031848          0.00323496          0.00527536\n     65  0.00050859   2.65  0.000508545  2.67   0.000508766  3.37\n    129  0.000128298  1.99  0.0001283    1.99   0.000128302  1.99\n    257  3.21476e-05  2.0   3.21478e-05  2.0    3.21489e-05  2.0\n    513  8.04148e-06  2.0   8.04149e-06  2.0    8.04146e-06  2.0\n   1025  2.00819e-06  2.0   2.00819e-06  2.0    2.0082e-06   2.0\n   2049  5.02099e-07  2.0   5.07555e-07  1.98   7.76833e-07  1.37\n\nssprk 33 (CFL = 0.8)\n\n  #dofs     L1 error           L2 error          Linf error\n-------  -----------  ----  -----------  ----  ------------  ----\n     33  0.00180086         0.00220063          0.00456292\n     65  7.86938e-06  7.84  7.87036e-06  8.13   7.86781e-06  9.18\n    129  7.35859e-07  3.42  7.35864e-07  3.42   7.35871e-07  3.42\n    257  8.24117e-08  3.16  8.24119e-08  3.16   8.24142e-08  3.16\n    513  9.97977e-09  3.05  9.97981e-09  3.05   9.97994e-09  3.05\n   1025  1.23568e-09  3.01  1.23571e-09  3.01   1.23577e-09  3.01\n   2049  1.54156e-10  3.0   1.54186e-10  3.0    1.54239e-10  3.0\n"
  },
  {
    "path": "prm/verification/linear_transport-time_stepping.prm",
    "content": "##\n#\n# Smooth linear transport problem in 1D.\n#\n# This configuration is used to verify our various time stepping schemes by\n# checking whether we observe the correct convergence order. We currently\n# support the following schemes:\n#\n#  - optimal explicit Runge Kutta (with recombined high-order fluxes):\n#     * \"erk 11\": one stage, first order\n#     * \"erk 22\": two stages, second order\n#     * \"erk 33\": three stages, third order\n#     * \"erk 43\": four stages, third order (fourth order for linear problems)\n#     * \"erk 54\": five stages, fourth order\n#\n#  - strong stability preserving Runge Kutta schemes:\n#     * \"ssprk 22\": two stages, second order\n#     * \"ssprk 33\": three stages, third order\n#\n##\n\n\nsubsection A - TimeLoop\n  set basename             = linear_transport-time_stepping\n\n  set enable compute error = true\n  set error normalize      = true\n\n  set enable output full   = false\n\n  set final time           = 2.00\n  set timer granularity    = 2.00\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = scalar conservation\n  set flux      = function\n  subsection function\n    set derivative approximation delta = 1e-10\n    set expression                     = u\n  end\nend\n\n\nsubsection C - Discretization\n  set finite element ansatz = cG Q1\n\n  set geometry            = rectangular domain\n\n  set mesh refinement     = 11\n\n  subsection rectangular domain\n    set boundary condition left   = periodic\n    set boundary condition right  = periodic\n    set position bottom left      = 0\n    set position top right        = 6.28318530718\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = function\n  set direction     = 1\n  set position      = 1\n\n  subsection function\n    set expression = sin(x-t)\n  end\nend\n\n\nsubsection F - HyperbolicModule\n  subsection indicator\n    set evc factor = 0\n  end\n  subsection limiter\n    set iterations        = 2\n    set relaxation factor = 1\n  end\n  subsection riemann solver\n    set use averaged entropy = false\n    set use greedy wavespeed = false\n    set random entropies = 0\n  end\nend\n\n\nsubsection H - TimeIntegrator\n# set cfl max               = 0.05 # for erk 11\n# set cfl min               = 0.05 # for erk 11\n# set time stepping scheme  = erk 11\n\n# set cfl max               = 0.20 # for erk 22\n# set cfl min               = 0.20 # for erk 22\n# set time stepping scheme  = erk 22\n\n  set cfl max               = 0.80 # all others\n  set cfl min               = 0.80 # all others\n  set time stepping scheme  = erk 33\n# set time stepping scheme  = erk 43\n# set time stepping scheme  = erk 54\n# set time stepping scheme  = ssprk 22\n# set time stepping scheme  = ssprk 33\n\n  set cfl recovery strategy = none\nend\n"
  },
  {
    "path": "prm/verification/navier_stokes-becker_solution-erk33.baseline",
    "content": "#dofs = 401, double, ERK(3,3,1), baseline, Harten entropy\nt     = 2.000140025099357\nLinf  = 0.0003372470374638317\nL1    = 3.541259068578513e-05\nL2    = 9.597495882465971e-05\n\n#dofs = 801, double, ERK(3,3,1), baseline, Harten entropy\nt     = 2.000009004954811\nLinf  = 8.620046282069202e-05\nL1    = 8.874545831538461e-06\nL2    = 2.40834778979447e-05\n\n#dofs = 1601, double, ERK(3,3,1), baseline, Harten entropy\nt     = 2.000007800561542\nLinf  = 2.187785014689407e-05\nL1    = 2.241601776673869e-06\nL2    = 6.037222741680699e-06\n\n#dofs = 3201, double, ERK(3,3,1), baseline, Harten entropy\nt     = 2.000007204741094\nLinf  = 5.504406657919105e-06\nL1    = 5.641050845408953e-07\nL2    = 1.511679454267349e-06\n\n#dofs = 6401, double, ERK(3,3,1), baseline, Harten entropy\nt     = 2.000006907348785\nLinf  = 1.5405603012915e-06\nL1    = 1.748642689697854e-07\nL2    = 4.148667770950692e-07\n"
  },
  {
    "path": "prm/verification/navier_stokes-becker_solution-erk33.prm",
    "content": "##\n#\n# Navier Stokes (optimized polytropic gas EOS) benchmark:\n#\n# An analytic solution of the compressible Navier-Stokes system as\n# described in @cite Becker1922. We report the final, normalized L1, L2,\n# L\\infty error norms summed up over the rho, m, and E components.\n#\n# This configuration uses an explicit ERK(3, 3, 1) timestepping.\n# Expected results are reported in euler-becker_solution-erk33.baseline\n#\n##\n\nsubsection A - TimeLoop\n  set basename             = becker_solution-erk33\n\n  set enable compute error = true\n  set error normalize      = true\n  set error quantities     = rho, m, E\n\n  set enable output full   = true\n\n  set final time           = 2.00\n  set timer granularity    = 2.00\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = navier stokes\n\n  set gamma     = 1.4\n  set mu        = 0.01\n  set lambda    = 0\n  set kappa     = 1.866666666666666e-2\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 5\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = -0.25\n    set position top right        =  0.25\n    set subdivisions x            = 25\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = becker solution\n  set direction     = 1\n  set position      = -0.125\n\n  subsection becker solution\n    set mu                      = 0.01\n    set velocity galilean frame = 0.125\n    set density left            = 1\n    set velocity left           = 1\n    set velocity right          = 0.259259259259\n  end\nend\n\n\nsubsection F - HyperbolicModule\n  subsection limiter\n    set iterations            = 2\n    set newton max iterations = 2\n    set newton tolerance      = 1e-10\n    set relaxation factor     = 8\n  end\nend\n\n\nsubsection G - ParabolicModule\n  set tolerance             = 1e-14\n  set tolerance linfty norm = false\n\n  set multigrid velocity    = false\n  set multigrid energy      = false\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.10\n  set cfl max               = 0.10\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang erk 33 cn\nend\n\n\nsubsection J - VTUOutput\n  set manifolds      =\n  set use mpi io     = true\n  set schlieren beta = 10\nend\n"
  },
  {
    "path": "prm/verification/shallow_water-paraboloid_1d-erk33.baseline",
    "content": "  #dofs     L1 error           L2 error          Linf error\n-------  -----------  ----  -----------  ----  ------------  ----\n    401  0.000215502        0.000245989         0.00094415\n    801  8.46805e-05  1.35  9.82305e-05  1.32   0.000459824  1.04\n   1601  3.83434e-05  1.14  4.59378e-05  1.1    0.000227545  1.01\n   3201  1.52898e-05  1.33  1.8738e-05   1.29   0.000113121  1.01\n   6401  6.98644e-06  1.13  8.40228e-06  1.16   5.13108e-05  1.14\n  12801  2.65194e-06  1.4   3.38499e-06  1.31   3.19078e-05  0.69\n  25601  8.57627e-07  1.63  1.37906e-06  1.3    1.43279e-05  1.16\n\n\n#dofs = 401, double, ERK(3,3,1), baseline\nt     = 1346.514692196049\nLinf  = 0.0009441504610619158\nL1    = 0.0002155023299383154\nL2    = 0.000245988674388453\n\n#dofs = 801, double, ERK(3,3,1), baseline\nt     = 1345.982563025663\nLinf  = 0.0004598235357980698\nL1    = 8.468050630138719e-05\nL2    = 9.823052351414305e-05\n\n#dofs = 1601, double, ERK(3,3,1), baseline\nt     = 1345.837468000477\nLinf  = 0.0002275451277399089\nL1    = 3.834335990314795e-05\nL2    = 4.593781608357397e-05\n\n#dofs = 3201, double, ERK(3,3,1), baseline\nt     = 1345.767149162498\nLinf  = 0.0001131206979416342\nL1    = 1.528984553877238e-05\nL2    = 1.873803758115697e-05\n\n#dofs = 6401, double, ERK(3,3,1), baseline\nt     = 1345.743343696515\nLinf  = 5.131076639024887e-05\nL1    = 6.986435458096734e-06\nL2    = 8.402283017414819e-06\n\n#dofs = 12801, double, ERK(3,3,1), baseline\nt     = 1345.735287945488\nLinf  = 3.190781663192084e-05\nL1    = 2.651936906036756e-06\nL2    = 3.384986993489572e-06\n\n#dofs = 25601, double, ERK(3,3,1), baseline\nt     = 1345.7265264908\nLinf  = 1.43279407603478e-05\nL1    = 8.576270714638907e-07\nL2    = 1.379058054626865e-06\n"
  },
  {
    "path": "prm/verification/shallow_water-paraboloid_1d-erk33.prm",
    "content": "##\n#\n# Shallow water benchmark:\n#\n# A 1D benchmark configuration consisting of a planar surface flow in a\n# radially-symmetric paraboloid basin without friction. See Section 4.2.2\n# of [1] for details.\n#\n# This configuration uses an explicit ERK(3, 3, 1) timestepping. Expected\n# results are reported in shallow_water-paraboloid_1d-erk33.baseline\n#\n# [1] SWASHES: a compilation of shallow water analytic solutions for\n#     hydraulic and environmental studies, Olivier Delestre, et al.,\n#     International Journal for Numerical Methods in Fluids, Vol. 72(3), 2013\n#\n##\n\nsubsection A - TimeLoop\n  set basename             = paraboloid_1d-erk33\n\n  set enable compute error = true\n  set error normalize      = true\n  set error quantities     = h\n\n  set enable output full   = true\n\n  set final time           = 1345.71\n  set timer granularity    = 1345.71\nend\n\nsubsection B - Equation\n  set dimension                    = 1\n  set equation                     = shallow water\n\n  set gravity                      = 9.81\n  set manning friction coefficient = 0\n\n  set reference water depth        = 10\n  set dry state relaxation small   = 1e2\n  set dry state relaxation large   = 1e4\nend\n\nsubsection C - Discretization\n  set geometry            = rectangular domain\n  set mesh refinement     = 6\n\n  subsection rectangular domain\n    set boundary condition left   = do nothing\n    set boundary condition right  = do nothing\n\n    set position bottom left      = 0\n    set position top right        = 10000\n    set subdivisions x            = 25\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = paraboloid\n  set direction     = 1\n  set position      = 0\n\n  subsection paraboloid\n    set free surface radius = 3000\n    set paraboloid length   = 10000\n    set speed               = 2\n    set water height        = 10\n  end\nend\n\nsubsection F - HyperbolicModule\n  subsection limiter\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.50\n  set cfl max               = 0.50\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "prm/verification/shallow_water-paraboloid_2d-erk33.baseline",
    "content": "   #dofs    L1 error          L2 error          Linf error\n--------  ----------  ----  ----------  ----  ------------  ----\n    4225  0.0632972         0.0622247           0.091812\n   16641  0.0172783   1.87  0.0175341   1.83    0.0370996   1.31\n   66049  0.00509554  1.76  0.00537588  1.71    0.0196472   0.92\n  263169  0.00173776  1.55  0.00196082  1.46    0.00993443  0.98\n 1050625  0.00070257  1.31  0.00080927  1.28    0.00571388  0.80\n\n\n#dofs =  4225, double, ERK(3,3,1), baseline\nt     = 13.47396197633032\nLinf  = 0.09181204749440396\nL1    = 0.06329717173997017\nL2    = 0.06222473052954026\n\n#dofs = 16641, double, ERK(3,3,1), baseline\nt     = 13.45864652958581\nLinf  = 0.03709957129940196\nL1    = 0.01727828664996339\nL2    = 0.01753408783895094\n\n#dofs = 66049, double, ERK(3,3,1), baseline\nt     = 13.45948479380375\nLinf  = 0.01964718057204452\nL1    = 0.005095544329384104\nL2    = 0.005375880806002048\n\n#dofs = 263169, double, ERK(3,3,1), baseline\nt     = 13.45737589319985\nLinf  = 0.00993443190067508\nL1    = 0.00173776336859641\nL2    = 0.001960817815654174\n\n#dofs = 1050625, double, ERK(3,3,1), baseline\nt     = 13.4572098904235\nLinf  = 0.005713879880461068\nL1    = 0.0007025749646802337\nL2    = 0.0008092714384943688\n"
  },
  {
    "path": "prm/verification/shallow_water-paraboloid_2d-erk33.prm",
    "content": "##\n#\n# Shallow water benchmark:\n#\n# A 2D benchmark configuration consisting of a planar surface flow in a\n# radially-symmetric paraboloid basin without friction. See Section 4.2.2\n# of [1] for details.\n#\n# This configuration uses an explicit ERK(3, 3, 1) timestepping. Expected\n# results are reported in shallow_water-paraboloid_2d-erk33.baseline\n#\n# [1] SWASHES: a compilation of shallow water analytic solutions for\n#     hydraulic and environmental studies, Olivier Delestre, et al.,\n#     International Journal for Numerical Methods in Fluids, Vol. 72(3), 2013\n#\n##\n\nsubsection A - TimeLoop\n  set basename             = paraboloid_2d-erk33\n\n  set enable compute error = true\n  set error normalize      = true\n  set error quantities     = h\n\n  set enable output full   = true\n\n  set final time           = 13.45710440\n  set timer granularity    = 13.45710440\nend\n\nsubsection B - Equation\n  set dimension                    = 2\n  set equation                     = shallow water\n\n  set gravity                      = 9.81\n  set manning friction coefficient = 0\n\n  set reference water depth        = 0.1\n  set dry state relaxation small   = 1e2\n  set dry state relaxation large   = 1e4\nend\n\nsubsection C - Discretization\n  set geometry            = rectangular domain\n  set mesh refinement     = 8\n\n  subsection rectangular domain\n    set boundary condition bottom = do nothing\n    set boundary condition left   = do nothing\n    set boundary condition right  = do nothing\n    set boundary condition top    = do nothing\n\n    set position bottom left      = 0, 0\n    set position top right        = 4, 4\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = paraboloid\n  set position      = 2, 2\n  set direction     = 1, 0\n\n  subsection paraboloid\n    set eta                 = 0.5\n    set free surface radius = 1\n    set water height        = 0.1\n  end\nend\n\nsubsection F - HyperbolicModule\n  subsection limiter\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.50\n  set cfl max               = 0.50\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "prm/verification/shallow_water-ritter_dam_break-erk33.baseline",
    "content": "  #dofs     L1 error           L2 error          Linf error\n-------  -----------  ----  -----------  ----  ------------  ----\n    401  0.00042991         0.000842541         0.00699379\n    801  0.00013527   1.67  0.000329108  1.36   0.00357062   0.97\n   1601  4.2499e-05   1.67  0.000138633  1.25   0.00229125   0.64\n   3201  2.03544e-05  1.06  6.23242e-05  1.15   0.00113704   1.01\n   6401  6.5828e-06   1.63  2.59768e-05  1.26   0.000635846  0.84\n  12801  1.8519e-06   1.83  1.09455e-05  1.25   0.000371087  0.78\n  25601  7.64516e-07  1.28  4.92926e-06  1.15   0.00023014   0.69\n\n\n#dofs = 25601, double, ERK(3,3,1), baseline\nt     = 6.000606077659553\nLinf  = 0.0002301396412329737\nL1    = 7.645156937474743e-07\nL2    = 4.929260005212694e-06\n\n#dofs = 401, double, ERK(3,3,1), baseline\nt     = 6.016540076696267\nLinf  = 0.006993789705593903\nL1    = 0.0004299097637166779\nL2    = 0.000842540841598642\n\n#dofs = 801, double, ERK(3,3,1), baseline\nt     = 6.012733558261776\nLinf  = 0.003570615878821168\nL1    = 0.0001352702011706505\nL2    = 0.0003291081491479276\n\n#dofs = 1601, double, ERK(3,3,1), baseline\nt     = 6.001190305055133\nLinf  = 0.002291249860502005\nL1    = 4.249901125077142e-05\nL2    = 0.0001386325510662635\n\n#dofs = 3201, double, ERK(3,3,1), baseline\nt     = 6.002065094435731\nLinf  = 0.001137042397958594\nL1    = 2.035435119886288e-05\nL2    = 6.232415634990908e-05\n\n#dofs = 6401, double, ERK(3,3,1), baseline\nt     = 6.00103498445957\nLinf  = 0.0006358459700208166\nL1    = 6.58279865969136e-06\nL2    = 2.597677530226299e-05\n\n#dofs = 12801, double, ERK(3,3,1), baseline\nt     = 6.000817986311765\nLinf  = 0.0003710871311507838\nL1    = 1.851896269041516e-06\nL2    = 1.094548861489066e-05\n"
  },
  {
    "path": "prm/verification/shallow_water-ritter_dam_break-erk33.prm",
    "content": "##\n#\n# Shallow water benchmark:\n#\n# Ritter's 1D dam break solution without friciton is a one-dimensional\n# analytical solution. See Section 7.3 in [1]\n#\n# This configuration uses an explicit ERK(3, 3, 1) timestepping. Expected\n# results are reported in shallow_water_ritter_dam_break-erk33.baseline\n#\n# [1] Well-Balanced Second-Order Finite Element Approximation of the\n#     Shallow Water Equations with Friction, Jean-Luc Guermond et al., Vol.\n#     40(6), 2018\n#\n##\n\nsubsection A - TimeLoop\n  set basename             = ritter_dam_break-erk33\n\n  set enable compute error = true\n  set error normalize      = true\n  set error quantities     = h\n\n  set enable output full   = true\n\n  set final time           = 6.0\n  set timer granularity    = 6.0\nend\n\nsubsection B - Equation\n  set dimension                    = 1\n  set equation                     = shallow water\n\n  set gravity                      = 9.81\n  set manning friction coefficient = 0\n\n  set reference water depth        = 0.005\n  set dry state relaxation small   = 1e2\n  set dry state relaxation large   = 1e4\nend\n\nsubsection C - Discretization\n  set geometry            = rectangular domain\n  set mesh refinement     = 6\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = 0\n    set position top right        = 10\n    set subdivisions x            = 25\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = ritter dam break\n  set direction     = 1\n  set position      = 5\n\n  subsection ritter dam break\n    set time initial = 1\n    set left water depth = 0.005\n  end\nend\n\nsubsection F - HyperbolicModule\n  subsection limiter\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.5\n  set cfl max               = 0.5\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "prm/verification/shallow_water-smooth_vortex-erk33.baseline",
    "content": "  #dofs     L1 error           L2 error          Linf error\n-------  -----------  ----  -----------  ----  ------------  ----\n   4225  0.000627538        0.00342288          0.035756\n  16641  8.40551e-05  2.9   0.000488458  2.81   0.00672828   2.41\n  66049  1.09308e-05  2.94  6.41716e-05  2.93   0.00102196   2.72\n 263169  1.42122e-06  2.94  8.37374e-06  2.94   0.000164031  2.64\n1050625  2.93908e-07  2.27  1.36509e-06  2.62   0.000124435  0.4\n\n\n#dofs =  4225, double, ERK(3,3,1), baseline\nt     = 2.002751204213519\nLinf  = 0.0357560220931379\nL1    = 0.0006275378708485086\nL2    = 0.003422878427058578\n\n#dofs = 16641, double, ERK(3,3,1), baseline\nt     = 2.002925499297903\nLinf  = 0.006728281805006476\nL1    = 8.405513707451128e-05\nL2    = 0.0004884583861034918\n\n#dofs = 66049, double, ERK(3,3,1), baseline\nt     = 2.001400929796003\nLinf  = 0.001021964552219789\nL1    = 1.093081214458911e-05\nL2    = 6.41716198204432e-05\n\n#dofs = 263169, double, ERK(3,3,1), baseline\nt     = 2.00067382263292\nLinf  = 0.0001640307890033918\nL1    = 1.42121975972531e-06\nL2    = 8.37374122096358e-06\n\n#dofs = 1050625, double, ERK(3,3,1), baseline\nt     = 2.000325467759434\nLinf  = 0.0001244354289825119\nL1    = 2.939081866190351e-07\nL2    = 1.365089066710311e-06\n"
  },
  {
    "path": "prm/verification/shallow_water-smooth_vortex-erk33.prm",
    "content": "##\n#\n# Shallow water benchmark:\n#\n# The smooth vortex is an analytic solution of the shallow water equations.\n# We compute the vortex on the square [-5, 5]^2 initially centered at\n# (-1,-1) with a strength of beta=5, and moving in diagonal (1,1) direction\n# with mach number 1. At final time t=2 the vortex is located at (1, 1). We\n# report the final, normalized L1, L2, L\\infty error norms summed up over\n# all components.\n#\n# This configuration uses an explicit ERK(3, 3, 1) timestepping.\n# Expected results are reported in euler-isentropic_vortex-erk33.baseline\n#\n##\n\nsubsection A - TimeLoop\n  set basename             = smooth_vortex-erk33\n\n  set enable compute error = true\n  set error normalize      = true\n  set error quantities     = h, m_1, m_2\n\n  set enable output full   = true\n\n  set final time           = 2.0\n  set timer granularity    = 2.0\nend\n\nsubsection B - Equation\n  set dimension                    = 2\n  set equation                     = shallow water\n\n  set gravity                      = 9.81\n  set manning friction coefficient = 0\n\n  set reference water depth        = 2\n  set dry state relaxation small   = 1e2\n  set dry state relaxation large   = 1e4\nend\n\nsubsection C - Discretization\n  set geometry            = rectangular domain\n  set mesh refinement     = 8\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -6, -6\n    set position top right        =  6,  6\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = smooth vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection smooth vortex\n    set beta            = 2\n    set mach number     = 1\n    set reference depth = 2\n  end\nend\n\nsubsection F - HyperbolicModule\n  subsection limiter\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.25\n  set cfl max               = 0.25\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "scripts/create_convergence_tables",
    "content": "#!/usr/bin/env python\n##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2023 - 2024 by the ryujin authors\n##\n\nhelp_description = \"\"\"\nThis script performs a convergence analysis by running a number of\nsimulations with increasing refinement. It reads the output of each\nsimulation to find the errors and then creates a convergence rate table.\n\nExample usage:\n\n> ./create_convergence_tables --file ryujin.prm --initial 3 --final 7\n\nRuns the ./ryujin executable with configuration file ryujin.prm from\ninitial refinement 3 up to 7\n\n> ./create_convergence_tables --command \"mpirun ryujin\" [...]\n\nRuns the ./ryujin executable via mpirun\n\"\"\"\n\nimport os, sys\nimport numpy as np\nfrom tabulate import tabulate\nimport argparse, textwrap, re, time\n\n#\n# Command line arguments:\n#\n\nparser = argparse.ArgumentParser(\n    prog=\"error_table\",\n    formatter_class=argparse.RawDescriptionHelpFormatter,\n    description=textwrap.dedent(help_description),\n)\n\nparser.add_argument(\n    \"--command\",\n    type=str,\n    default=\"./ryujin\",\n    help=\"command to execute (default: ./ryujin)\",\n    required=False,\n)\n\nparser.add_argument(\n    \"--file\",\n    type=str,\n    default=\"ryujin.prm\",\n    help=\"configuration file supplied as first argument (default: ryujin.prm)\",\n    required=False,\n)\n\nparser.add_argument(\n    \"--initial\",\n    type=int,\n    default=3,\n    help=\"initial mesh refinement level (default: 3)\",\n    required=False,\n)\n\nparser.add_argument(\n    \"--final\",\n    type=int,\n    default=7,\n    help=\"final mesh refinement level (default: 7)\",\n    required=False,\n)\n\nargs = parser.parse_args()\n\ncommand = str(args.command)\nprm_file = str(args.file)\ninitial_refinement = args.initial\nfinal_refinement = args.final\n\n#\n# Collect script name and arguments for output:\n#\n\nscript_name = sys.argv[0] # The script name is at index 0\narguments = sys.argv[1:]  # The actual arguments are from index 1 onwards\narguments_string = \" \".join(arguments)\nscript_args = f\"\\nScript: {script_name}\\nArguments: {arguments_string}\\n\"\n\n\ndef main():\n    run_simulations()\n    compute_errors()\n    compute_rates()\n\n\noutput_files = [\"refinement_\" + str(i) + \".out\" for i in range(initial_refinement, final_refinement + 1)]\n\n\ndef run_simulations():\n    # Get mesh refinement line number in prm file\n    target_string = \"set mesh refinement \"\n    result = find_string_in_file(prm_file, target_string)\n\n    if result:\n        line_number, line = result\n    else:\n        print(f\"The string '{target_string}' was not found in the file.\")\n        quit()\n\n    # Run program for each refinement\n    for i in range(len(output_files)):\n        print(\"-- refinement \" + str(i + initial_refinement) + \" ...\", end=\"\")\n\n        # Updates prm file with new mesh refinement\n        os.system(\n            'sed -i \"'\n            + str(line_number)\n            + \"s/.*/  set mesh refinement     = \"\n            + str(i + initial_refinement)\n            + '/g\" '\n            + prm_file\n        )\n\n        # Run simulation\n        os.system(command + \" \" + prm_file + \" > \" + output_files[i])\n\n        print(\" done\")\n\n\ndef compute_errors():\n    # initialize list for dofs and errors\n    global dofs\n    global L1error\n    global L2error\n    global Linf_error\n\n    dofs = []\n    L1error = []\n    L2error = []\n    Linf_error = []\n\n    # define strings to be found in files\n    dofs_string = \"#dofs = \"\n    L1_string = \"L1    = \"\n    L2_string = \"L2    = \"\n    Linf_string = \"Linf  = \"\n\n    # loop through files\n    for input_filepath in output_files:\n        with open(input_filepath) as fp:\n            for cnt, ln in enumerate(fp):\n                if ln.strip().startswith(dofs_string):\n                    match_number = re.compile(\n                        r\"\\s-?\\ *[0-9]+\\.?[0-9]*(?:[Ee]\\ *-?\\ *[0-9]+)?\"\n                    )\n                    temp = [int(x) for x in re.findall(match_number, ln)]\n                    dofs.append(temp[0])\n                if ln.strip().startswith(L1_string):\n                    match_number = re.compile(\n                        r\"\\s-?\\ *[0-9]+\\.?[0-9]*(?:[Ee]\\ *-?\\ *[0-9]+)?\"\n                    )\n                    final_list = [float(x) for x in re.findall(match_number, ln)]\n                    L1error.append(final_list[0])\n                if ln.strip().startswith(L2_string):\n                    match_number = re.compile(\n                        r\"\\s-?\\ *[0-9]+\\.?[0-9]*(?:[Ee]\\ *-?\\ *[0-9]+)?\"\n                    )\n                    final_list = [float(x) for x in re.findall(match_number, ln)]\n                    L2error.append(final_list[0])\n                if ln.strip().startswith(Linf_string):\n                    match_number = re.compile(\n                        r\"\\s?\\ *[0-9]+\\.?[0-9]*(?:[Ee]\\ *-?\\ *[0-9]+)?\"\n                    )\n                    final_list = [float(x) for x in re.findall(match_number, ln)]\n                    Linf_error.append(final_list[0])\n\n\ndef compute_rates():\n    L1_rates = []\n    L2_rates = []\n    Linf_rates = []\n\n    for i, error in enumerate(L1error):\n        if i > 0:\n            L1_rates.append(np.log(L1error[i - 1] / L1error[i]) / np.log(2))\n            L2_rates.append(np.log(L2error[i - 1] / L2error[i]) / np.log(2))\n            Linf_rates.append(np.log(Linf_error[i - 1] / Linf_error[i]) / np.log(2))\n\n    #\n    # Create a formatted table with tabulate:\n    #\n\n    table = []\n    for i in range(len(dofs)):\n        if i == 0:\n            table.append(\n                [dofs[i], L1error[i], \"\", L2error[i], \"\", Linf_error[i], \"\"]\n            )\n        else:\n            table.append(\n                [\n                    dofs[i],\n                    L1error[i],\n                    round(L1_rates[i - 1], 2),\n                    L2error[i],\n                    round(L2_rates[i - 1], 2),\n                    Linf_error[i],\n                    round(Linf_rates[i - 1], 2),\n                ]\n            )\n\n    text_table = tabulate(\n        table,\n        headers=[\n            \"#dofs\",\n            \"L1 error\",\n            \"\",\n            \"L2 error\",\n            \"\",\n            \"Linf error\",\n            \"\",\n        ],\n    )\n\n    # Output table to console\n    print(\" \")\n    print(text_table)\n\n    # Output table to txt file\n    f = open(\"convergence_rates.txt\", \"w+\")\n    f.write(script_args)\n    f.write(\"\\n\")\n    f.write(text_table)\n    f.close()\n\n    latex_options = {\n        # column alignment: \"c\" (center), \"r\" (right), \"l\" (left)\n        \"colalign\": ( \"c\", \"c\", \"c\",),\n        # column separator as a vertical line (\"|\")\n        \"colsep\": (\"|\",),\n    }\n\n    tex_table = tabulate(\n        table,\n        headers=[\n            \"$I$\",\n            r\"$\\delta^1(T)$\",\n            \"\",\n            r\"$\\delta^2(T)$\",\n            \"\",\n            r\"$\\delta^\\infty(T)$\",\n            \"\",\n        ],\n        tablefmt=\"latex_raw\",\n    )\n\n    f = open(\"convergence_rates.tex\", \"w+\")\n    f.write(tex_table)\n    f.close()\n\n\ndef find_string_in_file(file_path, target_string):\n    with open(file_path, \"r\") as file:\n        for line_number, line in enumerate(file, 1):\n            if target_string in line:\n                return line_number, line\n\n    # If the string is not found, return None\n    return None\n\n#\n# Call main and record total runtime:\n#\n\nstart_time = time.time()\nmain()\nprint(\"\\nTotal run time: %.2f seconds \" % (time.time() - start_time))\n"
  },
  {
    "path": "scripts/update-copyright",
    "content": "#!/bin/bash\n##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later\n## Copyright (C) 2015 - 2022 by Wolfgang Bangerth\n## Copyright (C) 2016 - 2022 by Timo Heister\n## Copyright (C) 2018 - 2024 by Matthias Maier\n## Copyright (C) 2024 by the ryujin authors\n##\n\nset -u\n\n#\n# Purpose: Update the copyright year of every file based on the last\n#          modification recorded in the git logs\n#\n\nif test ! -d source -o ! -d scripts -o ! -d tests ; then\n  echo \"This script must be run from the top-level directory of the repository.\"\n  exit\nfi\n\nprocesses=1\nuntil [[ \"$@\" == \"\" ]]; do\n  case $1 in\n    -j)\n      shift\n      if [[ \"$@\" == \"\" ]]; then\n        echo \"Error: »-j« must be followed by a number\" > /dev/stderr\n        echo \"Usage: update-copyright.sh [--pedantic] [-j N]\" > /dev/stderr\n        exit 1\n      fi\n      processes=\"${1}\"\n      shift;;\n    *)\n      echo \"Error: invalid option »$1«\" > /dev/stderr\n      echo \"Usage: update-copyright.sh [--pedantic] [-j N]\" > /dev/stderr\n      exit 1;;\n  esac\ndone\n\n#\n# A shell function that updates the copyright string for a given file $1:\n#\n\nupdate_copyright()\n{\n  file=\"${1}\"\n\n  if ! [ -f ${file} ]; then\n    echo \"Skipping ${file}: not a file\"\n    return\n  fi\n\n  if ! head -13 ${file} | grep -q \"^.. Copyright (C) .* by the ryujin authors\" ; then\n    echo \"Skipping ${file}: no ryujin copyright header\"\n    return\n  fi\n\n  #\n  # Get the last year this file was modified from the git log. We don't\n  # want to see patches that just updated the copyright year, thus find the\n  # first commit that\n  #  - does not mention both the words \"update\" and \"copyright\", as well as\n  #  - \"Update license headers\".\n  #\n\n  last_year=`git log -n 3 --date=short --format=\"format:%cd %s\" ${file} | \\\n    egrep -i -v \"update.*copyright|copyright.*update|update license headers\" | \\\n    head -n 1 | \\\n    perl -p -e 's/^(\\d\\d\\d\\d)-.*/\\1/g;'`\n\n  #\n  # It should not happen, that the grep removes all 3 most recent commits\n  # simultaneously but if it does then run the git log command again with\n  # full history:\n  #\n\n  [ -z \"$last_year\" ] && last_year=`git log --date=short --format=\"format:%cd %s\" ${file} | \\\n      egrep -i -v \"update.*copyright|copyright.*update|Update license headers\" | \\\n      head -n 1 | \\\n      perl -p -e 's/^(\\d\\d\\d\\d)-.*/\\1/g;'`\n\n  if [ -z \"$last_year\" ]; then\n    echo \"Skipping ${file}: internal error: could not determine last copyright year\"\n    return\n  fi\n\n  first_year=`git log --reverse --date=short --format=\"format:%cd %s\" ${file} | \\\n      head -n 1 | \\\n      perl -p -e 's/^(\\d\\d\\d\\d)-.*/\\1/g;'`\n\n  #\n  # Then, perform a more thorough search with `--diff-filter=A` to skip\n  # all but the first commit in which the file was created. We try to\n  # find and follow file renames with the `--follow` toggle.\n  #\n  # In corner cases, however, \"git log --follow\" can be way too\n  # aggressive. As a sanity check let's restrict the admissible date\n  # range to the date present in the file header, ${first_year}:\n  #\n  git_first_year=`git log --follow --diff-filter=A --date=short --format=\"format:%cd %s\" ${file} | \\\n      tail -n 1 | \\\n      perl -p -e 's/^(\\d\\d\\d\\d)-.*/\\1/g;'`\n\n  #\n  # If the above git command produced an output, use it. Otherwise fall\n  # back to ${first_year}:\n  #\n\n  first_year=\"${git_first_year:-${first_year}}\"\n\n  if [ -z \"$first_year\" ]; then\n    echo \"Skipping ${file}: internal error: could not determine first copyright year\"\n    return\n  fi\n\n  #\n  # Print a status message and update copyright line:\n  #\n\n  if [ \"${first_year}\" = \"${last_year}\" ]; then\n    echo \"Processing ${file}: ${last_year}\"\n    perl -pi -e \"s/(^.. Copyright \\(C\\)) \\d{4}( - \\d{4})?(, \\d{4}( - \\d{4})?)* by the ryujin authors/\\1 ${last_year} by the ryujin authors/g if 1..13;\" ${file}\n  else\n    echo \"Processing ${file}: ${first_year} - ${last_year}\"\n    perl -pi -e \"s/(^.. Copyright \\(C\\)) \\d{4}( - \\d{4})?(, \\d{4}( - \\d{4})?)* by the ryujin authors/\\1 ${first_year} - ${last_year} by the ryujin authors/g if 1..13;\" ${file}\n  fi\n}\n\n#\n# Run copyright update in parallel:\n#\n\nprocess()\n{\n  i=0\n  find ${1} -type f -regextype egrep -regex \"${2}\" | while read file; do\n    (( i=i%processes )); (( i++==0 )) && wait\n    update_copyright \"${file}\" &\n  done\n}\n\nprocess \".\"  \"CMakeLists.txt|CTestConfig.cmake\" update_copyright\nprocess \"cmake doc source scripts tests\" \".*\" update_copyright\n\n"
  },
  {
    "path": "source/CMakeLists.txt",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2020 - 2025 by the ryujin authors\n##\n\nconfigure_file(\n  ${CMAKE_CURRENT_SOURCE_DIR}/compile_time_options.h.in\n  ${CMAKE_CURRENT_BINARY_DIR}/compile_time_options.h\n  )\n\n#\n# Common and equation-independent source files and target:\n#\n\nset(COMMON_SOURCE_FILES\n  discretization.cc\n  equation_dispatch.cc\n  mpi_ensemble.cc\n  multicomponent_vector.cc\n  offline_data.cc\n  simd.cc\n  sparsity_pattern.cc\n  version_info.cc\n  )\n\nset_property(SOURCE version_info.cc APPEND PROPERTY COMPILE_DEFINITIONS\n  RYUJIN_VERSION=\"${RYUJIN_VERSION}\"\n  RYUJIN_GIT_REVISION=\"${GIT_REVISION}\"\n  RYUJIN_GIT_SHORTREV=\"${GIT_SHORTREV}\"\n  )\n\nadd_library(obj_common OBJECT ${COMMON_SOURCE_FILES})\ntarget_include_directories(obj_common PUBLIC\n  ${CMAKE_BINARY_DIR}/source/\n  ${CMAKE_CURRENT_SOURCE_DIR}\n  )\ndeal_ii_setup_target(obj_common)\ntarget_link_libraries(obj_common ${EXTERNAL_TARGETS})\n\n#\n# Common, equation-dependent source files:\n#\n\nset(DEPENDENT_SOURCE_FILES\n  hyperbolic_module.cc\n  initial_values.cc\n  mesh_adaptor.cc\n  postprocessor.cc\n  quantities.cc\n  solution_transfer.cc\n  time_integrator.cc\n  time_loop.cc\n  vtu_output.cc\n  )\n\n#\n# Equation-specific source files and compilation targets:\n#\n\nset(OBJECT_TARGETS)\nmacro(setup_equation EQUATION)\n  #\n  # obj_${EQUATION} object library: equation specific code:\n  #\n\n  add_subdirectory(${EQUATION})\n\n  #\n  # obj_${EQUATION}_dependent object library: equation dependent common code:\n  #\n\n  add_library(obj_${EQUATION}_dependent OBJECT ${DEPENDENT_SOURCE_FILES})\n  deal_ii_setup_target(obj_${EQUATION}_dependent)\n\n  target_link_libraries(obj_${EQUATION}_dependent\n    obj_${EQUATION} ${EXTERNAL_TARGETS}\n    )\n\n  list(APPEND OBJECT_TARGETS obj_${EQUATION} obj_${EQUATION}_dependent)\nendmacro()\n\nfile(GLOB _files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} CONFIGURE_DEPENDS */CMakeLists.txt)\nforeach(_file ${_files})\n  get_filename_component(_directory \"${_file}\" DIRECTORY)\n\n  string(TOUPPER \"${_directory}\" _directory_upper)\n  set(EQUATION_${_directory_upper} ${DEFAULT_ENABLE_EQUATIONS} CACHE BOOL \"Enable the equation »${_directory}« compute module\")\n\n  if(EQUATION_${_directory_upper})\n    setup_equation(\"${_directory}\")\n  endif()\nendforeach()\n\n#\n# The executable\n#\n\nadd_executable(ryujin main.cc)\ndeal_ii_setup_target(ryujin)\ntarget_link_libraries(ryujin obj_common ${OBJECT_TARGETS} ${EXTERNAL_TARGETS})\n\nset_target_properties(ryujin PROPERTIES\n  RUNTIME_OUTPUT_DIRECTORY \"${CMAKE_BINARY_DIR}/run\"\n  )\n\ninstall(TARGETS ryujin DESTINATION ${CMAKE_INSTALL_BINDIR})\n"
  },
  {
    "path": "source/Makefile",
    "content": "default: all\n.PHONY: default\n\n%:\n\t@cd .. && make $@\n.PHONY: %\n"
  },
  {
    "path": "source/README.md",
    "content": "<img align=\"right\" height=\"150\" src=\"../doc/logo.png\">\n\nsource directory\n================\n\nThe source directory is organized into generic header and source files\n(located directly in the `./source` directory and equation-specific modules\nlocated in subdirectories such as `./source/euler`, etc.\n"
  },
  {
    "path": "source/compile_time_options.h.in",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n/* Compile-time options: */\n\n#define NUMBER @NUMBER@\n\n/* Debug options: */\n\n#cmakedefine DEBUG_OUTPUT\n#cmakedefine DEBUG_EXPENSIVE_BOUNDS_CHECK\n#cmakedefine DEBUG_SYMMETRY_CHECK\n\n/* Compile-time parameters: */\n\n#cmakedefine DENORMALS_ARE_ZERO\n#cmakedefine FORCE_DEAL_II_SPARSE_MATRIX\n\n/* External packages: */\n\n#cmakedefine WITH_DEAL_II_THREADS\n#cmakedefine WITH_EOSPAC\n#cmakedefine WITH_GDAL\n#cmakedefine WITH_LIKWID\n#cmakedefine WITH_OPENMP\n\n/* Workarounds: */\n\n/*\n * Works around an issue when compiling ryujin with clang and attempting to\n * link against a deal.II library compiled with gcc. Both compilers seem to\n * disagree on how to name mangle the symbol leading to an undefined\n * reference when trying to link the final executable.\n */\n#cmakedefine BUG_COLLECT_PERIODIC_FACES_INSTANTIATION\n\n/*\n * This is an evil hack: enforce that we can provide our own\n * Patterns::Tools::Convert specialization by undefining the\n * DEAL_II_WITH_MAGIC_ENUM preprocessor definition\n */\n#include <deal.II/base/config.h>\n#ifdef DEAL_II_WITH_MAGIC_ENUM\n#undef DEAL_II_WITH_MAGIC_ENUM\n#endif\n"
  },
  {
    "path": "source/convenience_macros.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <deal.II/base/function.h>\n#include <deal.II/lac/vector.h>\n\nnamespace ryujin\n{\n#ifndef DOXYGEN\n  namespace\n  {\n    template <int dim, typename Number, typename Callable>\n    class ToFunction : public dealii::Function<dim, Number>\n    {\n    public:\n      ToFunction(const Callable &callable, const unsigned int k)\n          : dealii::Function<dim, Number>(1)\n          , callable_(callable)\n          , k_(k)\n      {\n      }\n\n      Number value(const dealii::Point<dim> &point,\n                   unsigned int /*component*/) const override\n      {\n        return callable_(point)[k_];\n      }\n\n    private:\n      const Callable callable_;\n      const unsigned int k_;\n    };\n\n\n    template <int dim, typename Number, typename Callable>\n    class ToVectorFunction : public dealii::Function<dim, Number>\n    {\n    public:\n      ToVectorFunction(const Callable &callable, const unsigned int components)\n          : dealii::Function<dim, Number>(components)\n          , callable_(callable)\n      {\n      }\n\n      Number value(const dealii::Point<dim> &point,\n                   unsigned int component) const override\n      {\n        return callable_(point)[component];\n      }\n\n      void vector_value(const dealii::Point<dim> &point,\n                        dealii::Vector<double> &values) const override\n      {\n        AssertDimension(values.size(), this->n_components);\n\n        const auto temp = callable_(point);\n        for (unsigned int k = 0; k < this->n_components; ++k)\n          values(k) = temp[k];\n      }\n\n    private:\n      const Callable callable_;\n    };\n  } // namespace\n#endif\n\n  /**\n   * @name Various convenience wrappers for dealing with dealii::Function,\n   * dealii::Tensor:\n   */\n  //@{\n\n\n  /**\n   * Convenience wrapper that creates a (scalar) dealii::Function object\n   * out of a (fairly general) callable object returning array-like values.\n   * An example usage is given by the interpolation of initial values\n   * performed in InitialValues::interpolate_hyperbolic_vector() and\n   * InitialValues::interpolate_initial_precomputed_vector()\n   * ```\n   * for(unsigned int i = 0; i < problem_dimension; ++i)\n   *   dealii::VectorTools::interpolate(\n   *     dof_handler,\n   *     to_function<dim, Number>(callable, i),\n   *     U[i]);\n   * ```\n   *\n   * @param callable A callable object that provides an `operator(const\n   * Point<dim> &)` and returns an array or rank-1 tensor. More precisely,\n   * the return type must have a subscript operator `operator[]`.\n   *\n   * @param k Index describing the component that is returned by the\n   * function object.\n   *\n   * @ingroup Miscellaneous\n   */\n  template <int dim, typename Number, typename Callable>\n  ToFunction<dim, Number, Callable> to_function(const Callable &callable,\n                                                const unsigned int k)\n  {\n    return {callable, k};\n  }\n\n\n  /**\n   * Convenience wrapper that creates a vector-valued dealii::Function\n   * object out of a (fairly general) callable object returning array-like\n   * values. An example usage is given by the interpolation of initial\n   * values performed in InitialValues::interpolate_hyperbolic_vector() and\n   * InitialValues::interpolate_initial_precomputed_vector()\n   * ```\n   * dealii::VectorTools::interpolate(\n   *   dof_handler,\n   *   to_function<dim, Number>(callable, block_size),\n   *   block_vector);\n   * ```\n   *\n   * @param callable A callable object that provides an `operator(const\n   * Point<dim> &)` and returns an array or rank-1 tensor. More precisely,\n   * the return type must have a subscript operator `operator[]`.\n   *\n   * @param n_components number of components.\n   *\n   * @ingroup Miscellaneous\n   */\n  template <int dim, typename Number, typename Callable>\n  ToVectorFunction<dim, Number, Callable>\n  to_vector_function(const Callable &callable, const unsigned int n_components)\n  {\n    return {callable, n_components};\n  }\n\n\n  /**\n   * Contract a given rank-2 tensor flux_ij and a rank-1 tensor c_ij:\n   */\n  template <typename FT,\n            int problem_dim = FT::dimension,\n            typename TT = typename FT::value_type,\n            typename T = typename TT::value_type>\n  DEAL_II_ALWAYS_INLINE inline dealii::Tensor<1, problem_dim, T>\n  contract(const FT &flux_ij, const TT &c_ij)\n  {\n    dealii::Tensor<1, problem_dim, T> result;\n    for (unsigned int k = 0; k < problem_dim; ++k)\n      result[k] = flux_ij[k] * c_ij;\n    return result;\n  }\n\n\n  /**\n   * Add two given rank-2 tensors flux_left_ij and flux_right_ij:\n   */\n  template <typename FT, int problem_dim = FT::dimension>\n  DEAL_II_ALWAYS_INLINE inline FT add(const FT &flux_left_ij,\n                                      const FT &flux_right_ij)\n  {\n    FT result;\n    for (unsigned int k = 0; k < problem_dim; ++k)\n      result[k] = flux_left_ij[k] + flux_right_ij[k];\n    return result;\n  }\n\n  //@}\n} // namespace ryujin\n\n\n#ifndef DOXYGEN\nnamespace\n{\n  template <typename T>\n  class is_dereferenceable\n  {\n    template <typename C>\n    static auto test(...) -> std::false_type;\n\n    template <typename C>\n    static auto test(C *) -> decltype(*std::declval<C>(), std::true_type());\n\n  public:\n    using type = decltype(test<T>(nullptr));\n    static constexpr auto value = type::value;\n  };\n\n  template <typename T, typename>\n  auto dereference(T &t) -> decltype(dereference(*t)) &;\n\n  template <typename T>\n  auto dereference(T &t) -> T &\n    requires(!is_dereferenceable<T>::value)\n  {\n    return t;\n  }\n\n  template <typename T>\n  auto dereference(T &t) -> decltype(*t) &\n    requires is_dereferenceable<T>::value\n  {\n    return *t;\n  }\n} /* anonymous namespace */\n#endif\n\n/**\n * @name Macros for accessor definitions with automatic dereferencing\n */\n//@{\n\n/**\n * A convenience macro that automatically writes out an accessor (or\n * getter) function:\n * ```\n * const Foo& bar() const { return bar_; }\n * ```\n * or\n * ```\n * const Foo& bar() const { return *bar_; }\n * ```\n * depending on whether bar_ can be dereferenced, or not.\n *\n * @ingroup Miscellaneous\n */\n#define ACCESSOR_READ_ONLY(member)                                             \\\n  inline const auto &member() const                                            \\\n  {                                                                            \\\n    return dereference(member##_);                                             \\\n  }\n\n\n/**\n * Variant of the macro above that returns a mutable reference.\n *\n * @ingroup Miscellaneous\n */\n#define ACCESSOR(member)                                                       \\\n  inline auto &member()                                                        \\\n  {                                                                            \\\n    return dereference(member##_);                                             \\\n  }\n\n\n/**\n * Variant of the macro above that does not attempt to dereference the\n * underlying object.\n *\n * @ingroup Miscellaneous\n */\n#define ACCESSOR_READ_ONLY_NO_DEREFERENCE(member)                              \\\n  inline const auto &member() const                                            \\\n  {                                                                            \\\n    return member##_;                                                          \\\n  }\n\n\n/**\n * Variant of the macro above that takes two arguments, container and\n * member, and creates an accessor function.\n * ```\n * const Foo& member() const { return container_.member; }\n * ```\n * The function will dereference container and member if they are of\n * pointer type.\n *\n * @ingroup Miscellaneous\n */\n#define ACCESSOR_CONTAINER_READ_ONLY(container, member)                        \\\n  inline const auto &member() const                                            \\\n  {                                                                            \\\n    return dereference(dereference(container).member);                         \\\n  }\n\n\n//@}\n/**\n * @name Macros for compiler hints\n */\n//@{\n\n/**\n * Macro expanding to a `#pragma` directive that looks nicer in indented\n * code and can be used in other preprocessor macro definitions.\n *\n * @ingroup Miscellaneous\n */\n#define RYUJIN_PRAGMA(x) _Pragma(#x)\n\n\n/**\n * Compiler hint annotating a boolean to be likely true.\n *\n * Intended use:\n * ```\n * if (RYUJIN_LIKELY(thread_ready == true)) {\n *   // likely branch\n * }\n * ```\n *\n * @note The performance penalty of incorrectly marking a condition as\n * likely is severe. Use only if the condition is almost always true.\n * @ingroup Miscellaneous\n */\n#define RYUJIN_LIKELY(x) (__builtin_expect(!!(x), 1))\n\n\n/**\n * Compiler hint annotating a boolean expression to be likely false.\n *\n * Intended use:\n * ```\n * if (RYUJIN_UNLIKELY(thread_ready == false)) {\n *   // unlikely branch\n * }\n * ```\n *\n * @note The performance penalty of incorrectly marking a condition as\n * unlikely is severe. Use only if the condition is almost always false.\n * @ingroup Miscellaneous\n */\n#define RYUJIN_UNLIKELY(x) (__builtin_expect(!!(x), 0))\n\n\n/**\n * Injects a label into the generated assembly.\n *\n * @ingroup Miscellaneous\n */\n#define ASM_LABEL(label) asm(\"#\" label);\n\n//@}\n"
  },
  {
    "path": "source/cubic_spline.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#ifdef DEAL_II_WITH_GSL\n#include <gsl/gsl_spline.h>\n\nnamespace ryujin\n{\n  /**\n   * A cubic spline class implemented as a thin wrapper around the GSL\n   * spline library functions.\n   *\n   * Usage:\n   * @code\n   * std::vector<double> xs{0.0, 0.2, 0.4, 0.6, 0.8, 1.0};\n   * std::vector<double> ys{1.0, 0.2, 5.0, 2.0, 1.0, 10.0};\n   * CubicSpline spline(xs, ys);\n   *\n   * spline.eval(0.5);\n   * @endcode\n   *\n   * @ingroup Mesh\n   */\n  class CubicSpline\n  {\n  public:\n    /**\n     * Constructor.\n     *\n     * @pre The supplied vectors @p x and @p y must have the same size and\n     * must contain at least two elements. The vector @p x must be sorted\n     */\n    CubicSpline(const std::vector<double> &x,\n                const std::vector<double> &y) noexcept\n        : x_(x)\n        , y_(y)\n    {\n      AssertNothrow(x_.size() == y_.size(), dealii::ExcInternalError());\n      AssertNothrow(x_.size() >= 2, dealii::ExcInternalError());\n      AssertNothrow(std::is_sorted(x_.begin(), x_.end()),\n                    dealii::ExcInternalError());\n\n      spline = gsl_spline_alloc(gsl_interp_cspline, x_.size());\n      gsl_spline_init(spline, x_.data(), y_.data(), x_.size());\n\n      accel = gsl_interp_accel_alloc();\n    }\n\n    /**\n     * Copy constructor.\n     */\n    CubicSpline(const CubicSpline &copy)\n        : CubicSpline(copy.x_, copy.y_)\n    {\n    }\n\n    /**\n     * The copy assignment operator is deleted.\n     */\n    CubicSpline &operator=(const CubicSpline &) = delete;\n\n    /**\n     * Destructor.\n     */\n    ~CubicSpline()\n    {\n      gsl_interp_accel_free(accel);\n      gsl_spline_free(spline);\n    }\n\n    /**\n     * Evaluate the cubic spline at a given point @p x.\n     *\n     * @pre The point @p x must lie within the interval described by the\n     * largest and smallest support point supplied to the constructor.\n     */\n    inline double eval(double x) const\n    {\n      return gsl_spline_eval(spline, x, accel);\n    }\n\n  private:\n    const std::vector<double> x_;\n    const std::vector<double> y_;\n    gsl_spline *spline;\n    mutable gsl_interp_accel *accel;\n  };\n} // namespace ryujin\n\n#endif\n"
  },
  {
    "path": "source/diagonal_preconditioner.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2022 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"state_vector.h\"\n\n#include <deal.II/lac/la_parallel_block_vector.h>\n\nnamespace ryujin\n{\n  /**\n   * A preconditioner implementing a diagonal scaling used for the\n   * non-multigrid CG iteration.\n   *\n   * @ingroup ParabolicModule\n   */\n  template <typename Number>\n  class DiagonalPreconditioner\n  {\n  public:\n    /**\n     * @copydoc ryujin::ScalarHostVector\n     */\n    using ScalarHostVector = typename Vectors::ScalarHostVector<Number>;\n\n    /**\n     * @copydoc ryujin::BlockVector\n     */\n    using BlockHostVector = typename Vectors::BlockHostVector<Number>;\n\n    /**\n     * Constructor\n     */\n    DiagonalPreconditioner() = default;\n\n    /**\n     * Reinit with a scalar partitioner\n     */\n    void reinit(const std::shared_ptr<const dealii::Utilities::MPI::Partitioner>\n                    &scalar_partitioner)\n    {\n      diagonal_.reinit(scalar_partitioner);\n    }\n\n    /**\n     * Get access to the internal vector to be externally filled.\n     */\n    ScalarHostVector &scaling_vector()\n    {\n      return diagonal_;\n    }\n\n    /**\n     * Apply on a scalar vector.\n     */\n    void vmult(ScalarHostVector &dst, const ScalarHostVector &src) const\n    {\n      const auto n_owned = diagonal_.get_partitioner()->locally_owned_size();\n      AssertDimension(n_owned, src.get_partitioner()->locally_owned_size());\n      AssertDimension(n_owned, dst.get_partitioner()->locally_owned_size());\n\n      DEAL_II_OPENMP_SIMD_PRAGMA\n      for (unsigned int i = 0; i < n_owned; ++i)\n        dst.local_element(i) =\n            diagonal_.local_element(i) * src.local_element(i);\n    }\n\n    /**\n     * Apply on a block vector.\n     */\n    void vmult(BlockHostVector &dst, const BlockHostVector &src) const\n    {\n      const auto n_blocks = src.n_blocks();\n      AssertDimension(n_blocks, dst.n_blocks());\n\n      const auto n_owned = diagonal_.get_partitioner()->locally_owned_size();\n\n      for (unsigned int d = 0; d < n_blocks; ++d) {\n        AssertDimension(n_owned,\n                        src.block(d).get_partitioner()->locally_owned_size());\n        AssertDimension(n_owned,\n                        dst.block(d).get_partitioner()->locally_owned_size());\n\n        DEAL_II_OPENMP_SIMD_PRAGMA\n        for (unsigned int i = 0; i < n_owned; ++i)\n          dst.block(d).local_element(i) =\n              diagonal_.local_element(i) * src.block(d).local_element(i);\n      }\n    }\n\n  private:\n    ScalarHostVector diagonal_;\n  };\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/discretization.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2023 by the ryujin authors\n//\n\n#include \"discretization.template.h\"\n\nnamespace ryujin\n{\n  /* instantiations */\n  template class Discretization<1>;\n  template class Discretization<2>;\n  template class Discretization<3>;\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/discretization.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"convenience_macros.h\"\n#include \"geometries/geometry.h\"\n#include \"mpi_ensemble.h\"\n#include \"patterns_conversion.h\"\n\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/distributed/fully_distributed_tria.h>\n#include <deal.II/distributed/shared_tria.h>\n#include <deal.II/distributed/tria.h>\n#include <deal.II/hp/fe_collection.h>\n#include <deal.II/hp/mapping_collection.h>\n#include <deal.II/hp/q_collection.h>\n\n#include <memory>\n#include <set>\n\nnamespace ryujin\n{\n  /**\n   * An enum of type dealii::types::boundary_id that provides an mnemonic\n   * for prescribing different boundary conditions on faces.\n   *\n   * @note In deal.II boundary ids are prescribed on faces. However, in our\n   * stencil-based method we need such an information for individual\n   * boundary degrees of freedom. Thus, the face boundary indicator has to\n   * be translated to individual degrees of freedom which happens in\n   * OfflineData::prepare() when constructing the\n   * OfflineData::boundary_map_ object.\n   *\n   * @note OfflineData::boundary_map_ is a std::vector that stores all\n   * encountered boundary conditions for an individual degree of freedom.\n   * The individual algebraic constraint is applied in no particular order.\n   * It is thus important to ensure that neighboring boundary conditions,\n   * are compatible. For example, inflow conditions prescribed via a\n   * Boundary::dirichlet face neighboring a Boundary::no_slip face have to\n   * ensure that they prescribe a state compatible with the no slip\n   * condition, etc.\n   *\n   * @note Data structures in OfflineData are initialized with the ensemble\n   * subrange communicator stored in MPIEnsemble.\n   *\n   * @ingroup Mesh\n   */\n  enum Boundary : dealii::types::boundary_id {\n    /**\n     * The \"do nothing\" outflow boundary condition: no special treatment of\n     * the boundary degree of freedom. For stability reasons it is\n     * important to ensure that this boundary id is only prescribed on\n     * degrees of freedom with a velocity vector pointing outward of the\n     * computational domain <b>and</b> coming from the interior of the\n     * domain.\n     */\n    do_nothing = 0,\n\n    /**\n     * Prescribe periodic boundary conditions by identifying opposing\n     * degrees of freedom. This currently requires a mesh with \"standard\n     * orientation\".\n     */\n    periodic = 1,\n\n    /**\n     * On (free) slip boundary degrees of freedom we enforce a vanishing\n     * normal component of the momentum in the Euler module. This is done\n     * by explicitly removing the normal component of the momentum for the\n     * degree of freedom at the end of TimeStep::euler_step(). In the\n     * dissipation module \\f$v\\cdot n\\f$ is enforced strongly which leads\n     * to a natural boundary condition on the symmetric stress tensor:\n     * \\f$\\tau\\cdot\\mathbb{S}(v)\\cdot\\vec n\\f$.\n     */\n    slip = 2,\n\n    /**\n     * On no-slip boundary degrees of freedom we enforce a vanishing normal\n     * component of the momentum in the Euler module. This is done by\n     * explicitly removing the normal component of the momentum for the\n     * degree of freedom at the end of TimeStep::euler_step(). In the\n     * dissipation module a vanishing velocity \\f$v=0\\f$ is enforced\n     * strongly.\n     */\n    no_slip = 3,\n\n    /**\n     * On degrees of freedom marked as Dirichlet boundary we reset the\n     * state of the degree of freedom to the value of\n     * InitialData::initial_state(). Such Dirichlet conditions can only be\n     * meaningfully enforced as inflow conditions, i.e., the velocity\n     * vector associated with a Dirichlet boundary degree of freedom has to\n     * point into the computational domain, and no \"backward traveling\"\n     * shock front or other flow feature must reach a Dirichlet boundary\n     * degree of freedom during the computation.\n     */\n    dirichlet = 4,\n\n    /**\n     * On degrees of freedom marked as a \"dynamic\" boundary we distinguish\n     * four cases (for the compressible Euler equations or related PDEs):\n     *  - supersonic inflow, where we reset the state of a boundary degree\n     *    of freedom to the value returned by InitialData::initial_state().\n     *    This is equivalent to \"dirichlet\" boundary conditions.\n     *  - supersonic outflow, where we do nothing, similarly to the \"do\n     *    nothing\" boundary conditions.\n     *  - in case of subsonic in-, or outflow the state of a boundary\n     *    degree of freedom is translated into \"Riemann characteristics\"\n     *    and the values of all incoming characteristics are replaced by\n     *    the corresponding value of the state returned by\n     *    InitialData::initial_state().\n     */\n    dynamic = 5,\n\n    /**\n     * For the Euler and shallow Water equations: On degrees of freedom\n     * marked as \"dirichlet momentum\" boundary, we reset only the momentum\n     * of the degree of freedom to the value of\n     * InitialData::initial_state(). Where appropriate, we keep water\n     * height h, density rho, and internal energy e of the boundary state.\n     * Such conditions are used, for example, in many steady state problems\n     * for the shallow water equations with inflow conditions.\n     */\n    dirichlet_momentum = 6,\n\n    /**\n     * For the Euler and shallow Water equations: On degrees of freedom\n     * marked as \"dirichlet velocity\" boundary, we reset only the velocity\n     * of the degree of freedom to the value of\n     * InitialData::initial_state(). Where appropriate, we keep water\n     * height h, density rho, and internal energy e of the boundary state.\n     */\n    dirichlet_velocity = 7\n  };\n\n\n  /**\n   * An enum class for setting the finite element ansatz.\n   *\n   * @ingroup Mesh\n   */\n  enum class Ansatz {\n    /** cG Q1: continuous bi- (tri-) linear Lagrange elements */\n    cg_q1,\n\n    /** cG Q2: continuous bi- (tri-) quadratic Lagrange elements */\n    cg_q2,\n\n    /** cG Q3: continuous bi- (tri-) cubic Lagrange elements */\n    cg_q3,\n\n    /** dG Q1: discontinuous bi- (tri-) linear Lagrange elements */\n    dg_q1,\n\n    /** dG Q2: discontinuous bi- (tri-) quadratic Lagrange elements */\n    dg_q2,\n\n    /** dG Q3: discontinuous bi- (tri-) cubic Lagrange elements */\n    dg_q3\n  };\n\n  /**\n   * An enum class for setting the type of Triangulation that should be\n   * constructed.\n   *\n   * @ingroup Mesh\n   */\n  enum class MeshType {\n    /** Use serial dealii::Triangulation<dim> */\n    serial,\n    /** Use parallel dealii::parallel::shared::Triangulation<dim> */\n    parallel_shared,\n    /** Use parallel dealii::parallel::distributed::Triangulation<dim> */\n    parallel_distributed,\n    /** Use parallel dealii::parallel::fullydistributed::Triangulation<dim> */\n    parallel_fullydistributed\n  };\n} // namespace ryujin\n\n#ifndef DOXYGEN\nDECLARE_ENUM(ryujin::Boundary,\n             LIST({ryujin::Boundary::do_nothing, \"do nothing\"},\n                  {ryujin::Boundary::periodic, \"periodic\"},\n                  {ryujin::Boundary::slip, \"slip\"},\n                  {ryujin::Boundary::no_slip, \"no slip\"},\n                  {ryujin::Boundary::dirichlet, \"dirichlet\"},\n                  {ryujin::Boundary::dynamic, \"dynamic\"},\n                  {ryujin::Boundary::dirichlet_momentum, \"dirichlet momentum\"},\n                  {ryujin::Boundary::dirichlet_velocity,\n                   \"dirichlet velocity\"}));\n\nDECLARE_ENUM(ryujin::Ansatz,\n             LIST({ryujin::Ansatz::cg_q1, \"cG Q1\"},\n                  {ryujin::Ansatz::cg_q2, \"cG Q2\"},\n                  {ryujin::Ansatz::cg_q3, \"cG Q3\"},\n                  {ryujin::Ansatz::dg_q1, \"dG Q1\"},\n                  {ryujin::Ansatz::dg_q2, \"dG Q2\"},\n                  {ryujin::Ansatz::dg_q3, \"dG Q3\"}));\n\nDECLARE_ENUM(ryujin::MeshType,\n             LIST({ryujin::MeshType::serial, \"serial\"},\n                  {ryujin::MeshType::parallel_shared, \"parallel shared\"},\n                  {ryujin::MeshType::parallel_distributed,\n                   \"parallel distributed\"},\n                  {ryujin::MeshType::parallel_fullydistributed,\n                   \"parallel fullydistributed\"}));\n#endif\n\nnamespace ryujin\n{\n  /**\n   * This class is as a container for data related to the discretization,\n   * this includes the triangulation, finite element, mapping, and\n   * quadrature. After prepare() is called, the getter functions\n   * Discretization::triangulation(), Discretization::finite_element(),\n   * Discretization::mapping(), and Discretization::quadrature() return\n   * valid const references to the mentioned objects.\n   *\n   * The class uses dealii::ParameterAcceptor to handle a multitude of\n   * parameters to control the creation of meshes for a variety of\n   * benchmark configurations and to read in meshes in one of the formats\n   * supported by the deal.II library.\n   *\n   * @ingroup Mesh\n   */\n  template <int dim>\n  class Discretization : public dealii::ParameterAcceptor\n  {\n  public:\n    /**\n     * Constructor.\n     */\n    Discretization(const MPIEnsemble &mpi_ensemble,\n                   const std::string &subsection = \"/Discretization\");\n\n    /**\n     * Create the triangulation and set up the finite element, mapping and\n     * quadrature objects.\n     */\n    void prepare(const std::string &base_name);\n\n    /**\n     * A collection of mappings, finite elements, and quadratures that are\n     * set up by the Discretization class. We create a dedicated struct\n     * with all unique_ptr to keep the interface to\n     * Geometry::populate_hp_collections() sane.\n     */\n    struct Collection {\n      std::unique_ptr<const dealii::hp::MappingCollection<dim>> mapping;\n      std::unique_ptr<const dealii::hp::FECollection<dim>> finite_element_cg;\n      std::unique_ptr<const dealii::hp::FECollection<dim>> finite_element_dg;\n      std::unique_ptr<const dealii::hp::QCollection<dim>> quadrature;\n      std::unique_ptr<const dealii::hp::QCollection<dim>> quadrature_high_order;\n      std::unique_ptr<const dealii::hp::QCollection<dim>> nodal_quadrature;\n      std::unique_ptr<const dealii::hp::QCollection<1>> quadrature_1d;\n      std::unique_ptr<const dealii::hp::QCollection<1>> nodal_quadrature_1d;\n      std::unique_ptr<const dealii::hp::QCollection<dim - 1>> face_quadrature;\n      std::unique_ptr<const dealii::hp::QCollection<dim - 1>>\n          face_nodal_quadrature;\n    };\n\n    /**\n     * @name Accessors to data structures managed by this class.\n     */\n    //@{\n\n    /**\n     * Return a read-only const reference to the selected geometry.\n     */\n    ACCESSOR_READ_ONLY(selected_geometry)\n\n    /**\n     * Return a read-only const reference to the finite element ansatz.\n     */\n    ACCESSOR_READ_ONLY(ansatz)\n\n    /**\n     * Return a boolean indicating whether the chosen Ansatz space is\n     * discontinuous.\n     */\n    bool have_discontinuous_ansatz() const\n    {\n      switch (ansatz_) {\n        /* Continuous Ansatz: */\n      case Ansatz::cg_q1:\n        [[fallthrough]];\n      case Ansatz::cg_q2:\n        [[fallthrough]];\n      case Ansatz::cg_q3:\n        return false;\n\n        /* Discontinuous Ansatz: */\n      case Ansatz::dg_q1:\n        [[fallthrough]];\n      case Ansatz::dg_q2:\n        [[fallthrough]];\n      case Ansatz::dg_q3:\n        return true;\n      }\n\n      AssertThrow(false, dealii::ExcInternalError());\n      __builtin_trap();\n    }\n\n    /**\n     * Return the polynomial degree of the chosen finite element ansatz.\n     */\n    unsigned int polynomial_degree() const\n    {\n      switch (ansatz_) {\n      case Ansatz::cg_q1:\n        [[fallthrough]];\n      case Ansatz::dg_q1:\n        return 1;\n      case Ansatz::cg_q2:\n        [[fallthrough]];\n      case Ansatz::dg_q2:\n        return 2;\n      case Ansatz::cg_q3:\n        [[fallthrough]];\n      case Ansatz::dg_q3:\n        return 3;\n      }\n\n      AssertThrow(false, dealii::ExcInternalError());\n      __builtin_trap();\n    }\n\n    /**\n     * Return a mutable reference to the refinement variable.\n     */\n    ACCESSOR(refinement)\n\n    /**\n     * Return a mutable reference to the triangulation.\n     */\n    ACCESSOR(triangulation)\n\n    /**\n     * Return a read-only const reference to the triangulation.\n     */\n    ACCESSOR_READ_ONLY(triangulation)\n    /**\n     * Return a read-only const reference to the mapping.\n     *\n     * @note The accessor returns an MappingCollection object.\n     */\n    ACCESSOR_CONTAINER_READ_ONLY(collection_, mapping)\n\n    /**\n     * Return a read-only const reference to a continuous (\"cG\") variant of\n     * the selected finite element space.\n     *\n     * @note If the selected finite element space is continuous then this\n     * method simply returns the same object as finite_element().\n     *\n     * @note The accessor returns an FECollection object.\n     */\n    ACCESSOR_CONTAINER_READ_ONLY(collection_, finite_element_cg)\n\n    /**\n     * Return a read-only const reference to a discontinuous (\"dG\") variant\n     * of the selected finite element space.\n     *\n     * @note If the selected finite element space is discontinuous then\n     * this method simply returns the same object as finite_element().\n     *\n     * @note The accessor returns an FECollection object.\n     */\n    ACCESSOR_CONTAINER_READ_ONLY(collection_, finite_element_dg)\n\n    /**\n     * Return a read-only const reference to the selected finite element.\n     *\n     * @note The accessor returns an FECollection object.\n     */\n    const dealii::hp::FECollection<dim> &finite_element() const\n    {\n      if (have_discontinuous_ansatz())\n        return *collection_.finite_element_dg;\n      else\n        return *collection_.finite_element_cg;\n    }\n\n    /**\n     * Return a read-only const reference to the quadrature rule.\n     *\n     * @note The accessor returns an QCollection object.\n     */\n    ACCESSOR_CONTAINER_READ_ONLY(collection_, quadrature)\n\n    /**\n     * Return a read-only const reference to a highe order quadrature rule\n     * used for computing errors.\n     *\n     * @note The accessor returns an QCollection object.\n     */\n    ACCESSOR_CONTAINER_READ_ONLY(collection_, quadrature_high_order)\n\n    /**\n     * Return a read-only const reference to the nodal quadrature rule\n     * (Gauß Lobatto).\n     *\n     * @note The accessor returns an QCollection object.\n     */\n    ACCESSOR_CONTAINER_READ_ONLY(collection_, nodal_quadrature)\n\n    /**\n     * Return a read-only const reference to the 1D quadrature rule.\n     *\n     * @note The accessor returns an QCollection object.\n     */\n    ACCESSOR_CONTAINER_READ_ONLY(collection_, quadrature_1d)\n\n    /**\n     * Return a read-only const reference to the 1D nodal quadrature rule\n     * (Gauß Lobatto).\n     *\n     * @note The accessor returns an QCollection object.\n     */\n    ACCESSOR_CONTAINER_READ_ONLY(collection_, nodal_quadrature_1d)\n\n    /**\n     * Return a read-only const reference to the face quadrature rule.\n     *\n     * @note The accessor returns an QCollection object.\n     */\n    ACCESSOR_CONTAINER_READ_ONLY(collection_, face_quadrature)\n\n    /**\n     * Return a read-only const reference to the nodal face quadrature rule\n     * (Gauß Lobatto).\n     *\n     * @note The accessor returns an QCollection object.\n     */\n    ACCESSOR_CONTAINER_READ_ONLY(collection_, face_nodal_quadrature)\n\n  private:\n    //@}\n    /**\n     * @name Run time options\n     */\n    //@{\n\n    Ansatz ansatz_;\n    MeshType mesh_type_;\n\n    std::string geometry_;\n\n    unsigned int refinement_;\n\n    bool mesh_writeout_;\n    double mesh_distortion_;\n\n    //@}\n    /**\n     * @name Internal data:\n     */\n    //@{\n    //\n    const MPIEnsemble &mpi_ensemble_;\n\n    std::unique_ptr<dealii::Triangulation<dim>> triangulation_;\n\n    Collection collection_;\n\n    std::set<std::shared_ptr<Geometry<dim>>> geometry_list_;\n    std::shared_ptr<Geometry<dim>> selected_geometry_;\n\n    //@}\n\n    /**\n     * In the SolutionTransfer class we need writable access to the\n     * triangulation object in order to prepare data for mesh adaptation\n     * and checkpointing / restart. Work around this issue by declaring the\n     * solution transfer class to be a friend rather than changing the\n     * constructor, or augmenting the methods in SolutionTransfer.\n     */\n    template <typename Discretization, int dim_, typename Number_>\n    friend class SolutionTransfer;\n\n    /**\n     * For complex geometries with mixed finite elements (or when using\n     * FE_Nothing) we need to defer the setup of the hp::*Collection\n     * objects to the selected geometry. Thus, declare the Geometry class\n     * to be a friend so that it can set all the collection objects\n     * directly.\n     */\n    template <int dim_>\n    friend class Geometry;\n  };\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/discretization.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include <boost/random/detail/polynomial.hpp>\n#include <compile_time_options.h>\n\n#include \"discretization.h\"\n#include \"geometries/geometry_library.h\"\n\n#include <deal.II/base/quadrature_lib.h>\n#include <deal.II/fe/fe_dgq.h>\n#include <deal.II/fe/fe_nothing.h>\n#include <deal.II/fe/fe_q.h>\n#include <deal.II/fe/fe_simplex_p.h>\n#include <deal.II/fe/fe_tools.h>\n#include <deal.II/fe/mapping_fe.h>\n#if DEAL_II_VERSION_GTE(9, 7, 0)\n#include <deal.II/fe/mapping_p1.h>\n#endif\n#include <deal.II/fe/mapping_q.h>\n#include <deal.II/grid/grid_out.h>\n\n#include <random>\n\nnamespace ryujin\n{\n  using namespace dealii;\n\n  template <int dim>\n  Discretization<dim>::Discretization(const MPIEnsemble &mpi_ensemble,\n                                      const std::string &subsection)\n      : ParameterAcceptor(subsection)\n      , mpi_ensemble_(mpi_ensemble)\n  {\n    /* Options: */\n\n    ansatz_ = Ansatz::cg_q1;\n    add_parameter(\"finite element ansatz\",\n                  ansatz_,\n                  \"The finite element ansatz used for discretization. Valid \"\n                  \"choices are cG Q1, cG Q2, cG Q3.\");\n\n    mesh_type_ =\n        (dim == 1 ? MeshType::parallel_shared : MeshType::parallel_distributed);\n    add_parameter(\"mesh type\",\n                  mesh_type_,\n                  \"The triangulation class used. Valid choices are \\\"serial\\\", \"\n                  \"\\\"parallel shared\\\", \\\"parallel distributed\\\", \\\"parallel \"\n                  \"fullydistributed\\\".\");\n\n    if constexpr (dim == 1) {\n      geometry_ = \"rectangular domain\";\n    } else {\n      geometry_ = \"cylinder\";\n    }\n    add_parameter(\"geometry\",\n                  geometry_,\n                  \"Name of the geometry used to create the mesh. Valid names \"\n                  \"are given by any of the subsections defined below.\");\n\n    refinement_ = 5;\n    add_parameter(\"mesh refinement\",\n                  refinement_,\n                  \"number of refinement of global refinement steps\");\n\n    mesh_writeout_ = true;\n    add_parameter(\"mesh writeout\",\n                  mesh_writeout_,\n                  \"Write out shared coarse mesh to a GMSH *.msh file.\");\n\n    mesh_distortion_ = 0.;\n    add_parameter(\n        \"mesh distortion\", mesh_distortion_, \"Strength of mesh distortion\");\n\n    Geometries::populate_geometry_list<dim>(geometry_list_, subsection);\n  }\n\n\n  template <int dim>\n  void Discretization<dim>::prepare(const std::string &base_name)\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"Discretization<dim>::prepare()\" << std::endl;\n#endif\n\n    /* Select geometry: */\n\n    {\n      bool initialized = false;\n      for (auto &it : geometry_list_)\n        if (it->name() == geometry_) {\n          selected_geometry_ = it;\n          initialized = true;\n          break;\n        }\n\n      AssertThrow(\n          initialized,\n          ExcMessage(\"Could not find a geometry description with name \\\"\" +\n                     geometry_ + \"\\\"\"));\n    }\n\n    /* Set up Triangulation object: */\n\n    const auto smoothing =\n        dealii::Triangulation<dim>::limit_level_difference_at_vertices;\n\n    switch (mesh_type_) {\n    case MeshType::parallel_fullydistributed: {\n      triangulation_ = std::make_unique<\n          dealii::parallel::fullydistributed::Triangulation<dim>>(\n          mpi_ensemble_.ensemble_communicator());\n      triangulation_->set_mesh_smoothing(smoothing);\n    } break;\n\n    case MeshType::parallel_distributed: {\n      const auto settings = dealii::parallel::distributed::Triangulation<\n          dim>::Settings::construct_multigrid_hierarchy;\n      triangulation_ =\n          std::make_unique<dealii::parallel::distributed::Triangulation<dim>>(\n              mpi_ensemble_.ensemble_communicator(), smoothing, settings);\n    } break;\n\n    case MeshType::parallel_shared: {\n      const auto settings = static_cast<\n          typename dealii::parallel::shared::Triangulation<dim>::Settings>(\n          dealii::parallel::shared::Triangulation<dim>::partition_auto |\n          dealii::parallel::shared::Triangulation<\n              dim>::construct_multigrid_hierarchy);\n      /* Beware of the boolean: */\n      triangulation_ =\n          std::make_unique<dealii::parallel::shared::Triangulation<dim>>(\n              mpi_ensemble_.ensemble_communicator(),\n              smoothing,\n              /*artificial cells*/ true,\n              settings);\n    } break;\n\n    case MeshType::serial: {\n      AssertThrow(\n          mpi_ensemble_.n_ensemble_ranks() == 1,\n          ExcMessage(\n              \"The serial triangulation can only be used for serial \"\n              \"computations. If you want to run simulations with more than one \"\n              \"rank per ensemble, then please set \\\"mesh type\\\" to one of the \"\n              \"parallel triangulations supported by deal.II\"));\n\n      triangulation_ = std::make_unique<dealii::Triangulation<dim>>(smoothing);\n\n    } break;\n\n    default:\n      __builtin_trap();\n    }\n\n    /* Create and distribute mesh: */\n\n    auto &triangulation = *triangulation_;\n    selected_geometry_->create_coarse_triangulation(triangulation);\n\n    if (mesh_writeout_ && dealii::Utilities::MPI::this_mpi_process(\n                              mpi_ensemble_.ensemble_communicator()) == 0) {\n#ifdef DEAL_II_GMSH_WITH_API\n      GridOut grid_out;\n      grid_out.write_msh(triangulation, base_name + \"-coarse_grid.msh\");\n#else\n      GridOut grid_out;\n      GridOutFlags::Msh flags(/* write faces */ true, /* write lines */ true);\n      grid_out.set_flags(flags);\n      std::ofstream file(base_name + \"-coarse_grid.msh\");\n      grid_out.write_msh(triangulation, file);\n#endif\n    }\n\n    triangulation.refine_global(refinement_);\n\n    if (std::abs(mesh_distortion_) > 1.0e-10)\n      GridTools::distort_random(\n          mesh_distortion_, triangulation, false, std::random_device()());\n\n    const auto fe_degree = polynomial_degree();\n    const auto mapping_degree = fe_degree;\n    const auto quadrature_degree = fe_degree + 1;\n\n    /*\n     * First, let the selected geometry populate our hp::*Collection\n     * objects. If the method returns standard_quarilaterls, or\n     * standard_simplices, however, we need to do the setup ourselves:\n     */\n\n    const auto collection_type =\n        selected_geometry_->populate_hp_collections(fe_degree, collection_);\n\n    switch (collection_type) {\n    case Geometry<dim>::HP_Collection::populated_by_geometry: {\n      /*\n       * The geometry already populated the hp::*Collections\n       */\n\n      Assert(collection_.mapping, dealii::ExcInternalError());\n      Assert(collection_.finite_element_cg, dealii::ExcInternalError());\n      Assert(collection_.finite_element_dg, dealii::ExcInternalError());\n      Assert(collection_.quadrature, dealii::ExcInternalError());\n      Assert(collection_.quadrature_high_order, dealii::ExcInternalError());\n      Assert(collection_.nodal_quadrature, dealii::ExcInternalError());\n      Assert(collection_.quadrature_1d, dealii::ExcInternalError());\n      Assert(collection_.nodal_quadrature_1d, dealii::ExcInternalError());\n      Assert(collection_.face_quadrature, dealii::ExcInternalError());\n      Assert(collection_.face_nodal_quadrature, dealii::ExcInternalError());\n    } break;\n\n    case Geometry<dim>::HP_Collection::standard_quadrilaterals: {\n      /*\n       * Populate all collections with appropriate objects for the cG Qk, dG\n       * Qk finite element on purely quadrilateral, or hexahedral meshes:\n       */\n\n      collection_.finite_element_cg =\n          std::make_unique<hp::FECollection<dim>>(FE_Q<dim>(fe_degree));\n      collection_.finite_element_dg =\n          std::make_unique<hp::FECollection<dim>>(FE_DGQ<dim>(fe_degree));\n\n      collection_.mapping =\n          std::make_unique<dealii::hp::MappingCollection<dim>>(\n              MappingQ<dim>(mapping_degree));\n\n      collection_.quadrature = std::make_unique<hp::QCollection<dim>>(\n          QGauss<dim>(quadrature_degree));\n      collection_.quadrature_high_order =\n          std::make_unique<hp::QCollection<dim>>(\n              QGauss<dim>(quadrature_degree + 1));\n      collection_.nodal_quadrature = std::make_unique<hp::QCollection<dim>>(\n          QGaussLobatto<dim>(quadrature_degree));\n      collection_.quadrature_1d =\n          std::make_unique<hp::QCollection<1>>(QGauss<1>(quadrature_degree));\n      collection_.nodal_quadrature_1d = std::make_unique<hp::QCollection<1>>(\n          QGaussLobatto<1>(quadrature_degree));\n      collection_.face_quadrature = std::make_unique<hp::QCollection<dim - 1>>(\n          QGauss<dim - 1>(quadrature_degree));\n      collection_.face_nodal_quadrature =\n          std::make_unique<hp::QCollection<dim - 1>>(\n              QGaussLobatto<dim - 1>(quadrature_degree));\n    } break;\n\n    case Geometry<dim>::HP_Collection::standard_simplices: {\n      /*\n       * Populate all collections with appropriate objects for the cG Pk, dG\n       * Pk finite element on purely quadrilateral, or hexahedral meshes:\n       */\n\n      collection_.finite_element_cg =\n          std::make_unique<hp::FECollection<dim>>(FE_SimplexP<dim>(fe_degree));\n      collection_.finite_element_dg = std::make_unique<hp::FECollection<dim>>(\n          FE_SimplexDGP<dim>(fe_degree));\n\n      if (mapping_degree == 1) {\n#if DEAL_II_VERSION_GTE(9, 7, 0)\n        collection_.mapping =\n            std::make_unique<hp::MappingCollection<dim>>(MappingP1<dim>());\n#else\n        collection_.mapping = std::make_unique<hp::MappingCollection<dim>>(\n            MappingFE<dim>(FE_SimplexP<dim>(fe_degree)));\n#endif\n      } else {\n        collection_.mapping = std::make_unique<hp::MappingCollection<dim>>(\n            MappingFE<dim>(FE_SimplexP<dim>(fe_degree)));\n      }\n\n      collection_.quadrature = std::make_unique<hp::QCollection<dim>>(\n          QGaussSimplex<dim>(quadrature_degree));\n      collection_.quadrature_high_order =\n          std::make_unique<hp::QCollection<dim>>(\n              QGaussSimplex<dim>(quadrature_degree + 1));\n#if DEAL_II_VERSION_GTE(9, 7, 0)\n      collection_.nodal_quadrature = std::make_unique<hp::QCollection<dim>>(\n          FETools::compute_nodal_quadrature(\n              FE_SimplexP<dim>(quadrature_degree)));\n#else\n      AssertThrow(false,\n                  dealii::ExcMessage(\"Discretization: Simplex support requires \"\n                                     \"deal.II version 9.7.0 or newer\"));\n\n#endif\n      collection_.quadrature_1d = std::make_unique<hp::QCollection<1>>(\n          QGaussSimplex<1>(quadrature_degree));\n#if DEAL_II_VERSION_GTE(9, 7, 0)\n      collection_.nodal_quadrature_1d = std::make_unique<hp::QCollection<1>>(\n          QGaussLobatto<1>(quadrature_degree));\n#endif\n      collection_.face_quadrature = std::make_unique<hp::QCollection<dim - 1>>(\n          QGaussSimplex<dim - 1>(quadrature_degree));\n      if constexpr (dim == 1) {\n        collection_.face_nodal_quadrature =\n            std::make_unique<hp::QCollection<dim - 1>>(\n                QGaussLobatto<dim - 1>(quadrature_degree));\n      } else {\n#if DEAL_II_VERSION_GTE(9, 7, 0)\n        collection_.face_nodal_quadrature =\n            std::make_unique<hp::QCollection<dim - 1>>(\n                FETools::compute_nodal_quadrature(\n                    FE_SimplexP<dim - 1>(quadrature_degree)));\n#endif\n      }\n\n      return;\n    } break;\n    default:\n      __builtin_trap();\n    }\n  }\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/equation_dispatch.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2024 - 2025 by the ryujin authors\n//\n\n#include \"equation_dispatch.h\"\n\nryujin::EquationDispatch::Signals *ryujin::EquationDispatch::signals = nullptr;\n"
  },
  {
    "path": "source/equation_dispatch.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"time_loop.h\"\n\n#include <deal.II/base/mpi.h>\n#include <deal.II/base/parameter_acceptor.h>\n\n#include <algorithm>\n#include <boost/signals2.hpp>\n\n#include <string>\n\nnamespace ryujin\n{\n  /**\n   * The Dave error message\n   */\n  inline const std::string dave =\n      \"\\nDave, this conversation can serve no purpose anymore. Goodbye.\\n\\n\";\n\n  /**\n   * Dispatcher class that calls into the right TimeLoop depending on what\n   * has been set in the parameter file.\n   *\n   * When starting up the ryujin executable we are faced with the following\n   * difficulties:\n   *  - The TimeLoop class is templated in the equation Description and\n   *    dimension that have to be read from the `ryujin.prm` parameter\n   *    file.\n   *  - The final set of valid parameters that can be configured in the\n   *    `ryujin.prm` depend on the runtime parameters \"dimension\" and\n   *    \"equation\" themselves.\n   *\n   * We thus first read in two parameters from the parameter file:\n   * ```\n   * subsection B - Equation\n   *   set dimension           = ...\n   *   set equation            = ...\n   * end\n   * ```\n   * and then create an instance of the correct TimeLoop class, that takes\n   * the dimension and equation as template parameters.\n   *\n   * @ingroup TimeLoop\n   */\n  class EquationDispatch : dealii::ParameterAcceptor\n  {\n  public:\n    EquationDispatch()\n        : ParameterAcceptor(\"B - Equation\")\n    {\n      dimension_ = 0;\n      add_parameter(\"dimension\", dimension_, \"The spatial dimension\");\n      add_parameter(\"equation\", equation_, \"The PDE system\");\n\n      time_loop_executed_ = false;\n    }\n\n\n    ~EquationDispatch() override\n    {\n      if (signals) {\n        delete signals;\n        signals = nullptr;\n      }\n    }\n\n\n    /**\n     * Call create_parameter_files() for all registered equations.\n     */\n    static void create_parameter_files()\n    {\n      AssertThrow(signals != nullptr,\n                  dealii::ExcMessage(\n                      dave + \"No equation has been registered. Consequently, \"\n                             \"there is nothing for us to do.\\n\"));\n\n      if (signals != nullptr)\n        signals->create_parameter_files();\n    }\n\n\n    /**\n     * Register a create_parameter_files() callback.\n     */\n    template <typename Callable>\n    static void register_create_parameter_files(const Callable &callable)\n    {\n      if (signals == nullptr)\n        signals = new Signals;\n\n      signals->create_parameter_files.connect(callable);\n    }\n\n\n    /**\n     * Call dispatch() for all registered equations.\n     */\n    void dispatch(const std::string &parameter_file, const MPI_Comm &mpi_comm)\n    {\n      ParameterAcceptor::prm.parse_input(parameter_file,\n                                         \"\",\n                                         /* skip undefined */ true,\n                                         /* assert entries present */ false);\n\n      AssertThrow(dimension_ >= 1 && dimension_ <= 3,\n                  dealii::ExcMessage(dave +\n                                     \"The dimension parameter needs to be \"\n                                     \"either 1, 2, or 3, but we encountered »\" +\n                                     std::to_string(dimension_) + \"«\\n\"));\n\n      AssertThrow(signals != nullptr,\n                  dealii::ExcMessage(\n                      dave + \"No equation has been registered. Consequently, \"\n                             \"there is nothing for us to do.\\n\"));\n\n      if (signals != nullptr)\n        signals->dispatch(dimension_,\n                          equation_,\n                          parameter_file,\n                          mpi_comm,\n                          time_loop_executed_);\n\n      AssertThrow(time_loop_executed_ == true,\n                  dealii::ExcMessage(dave +\n                                     \"No equation was dispatched \"\n                                     \"with the chosen equation parameter »\" +\n                                     equation_ + \"«.\\n\"));\n    }\n\n\n    /**\n     * Register a dispatch() callback.\n     */\n    template <typename Callable>\n    static void register_dispatch(const Callable &callable)\n    {\n      if (signals == nullptr)\n        signals = new Signals;\n\n      signals->dispatch.connect(callable);\n    }\n\n  protected:\n    /**\n     * @name Internal data structures:\n     */\n    //@{\n\n    /**\n     * A structure that holds two Signals for equations:\n     *  - one for creating and running the appropriate timeloop\n     *  - the other signal is used for creating default parameter files.\n     */\n    struct Signals {\n      boost::signals2::signal<void()> create_parameter_files;\n\n      boost::signals2::signal<void(int /*dimension*/,\n                                   const std::string & /*equation*/,\n                                   const std::string & /*parameter file*/,\n                                   const MPI_Comm & /*MPI communicator*/,\n                                   bool & /*time loop executed*/)>\n          dispatch;\n    };\n\n    /*\n     * Note: as a static field the pointer is zero initialized before any\n     * static/global constructor is run.\n     */\n    static Signals *signals;\n\n  private:\n    //@}\n    /**\n     * @name Runtime parameters:\n     */\n    //@{\n\n    int dimension_;\n    std::string equation_;\n\n    //@}\n\n    bool time_loop_executed_;\n  };\n\n\n  /**\n   * Create default parameter files for the specified equation Description,\n   * dimension and number type. This function is called from the respective\n   * equation driver.\n   */\n  template <typename Description, int dim, typename Number>\n  void create_prm_files(const std::string &name,\n                        bool write_detailed_description)\n  {\n    {\n      /*\n       * Workaround: Add an entry to the \"A - TimeLoop\" section so that is\n       * shows up first.\n       */\n      auto &prm = dealii::ParameterAcceptor::prm;\n      prm.enter_subsection(\"A - TimeLoop\");\n      prm.declare_entry(\"basename\", \"test\");\n      prm.leave_subsection();\n\n      /*\n       * Create temporary objects for the sole purpose of populating the\n       * ParameterAcceptor::prm object.\n       */\n\n      ryujin::EquationDispatch equation_dispatch;\n      ryujin::TimeLoop<Description, dim, Number> time_loop(MPI_COMM_SELF);\n\n      /*\n       * Fix up \"equation\" entry:\n       */\n      prm.enter_subsection(\"B - Equation\");\n      prm.declare_entry(\"dimension\",\n                        std::to_string(dim),\n                        dealii::Patterns::Integer(),\n                        \"The spatial dimension\");\n      prm.declare_entry(\n          \"equation\", name, dealii::Patterns::Anything(), \"The PDE system\");\n      prm.set(\"dimension\", std::to_string(dim));\n      prm.set(\"equation\", name);\n      prm.leave_subsection();\n\n      std::string base_name = name;\n      std::ranges::replace(base_name, ' ', '_');\n      base_name += \"-\" + std::to_string(dim) + \"d\";\n\n      if (dealii::Utilities::MPI::this_mpi_process(MPI_COMM_SELF) == 0) {\n        const auto full_name =\n            \"default_parameters-\" + base_name + \"-description.prm\";\n        if (write_detailed_description)\n          prm.print_parameters(\n              full_name,\n              dealii::ParameterHandler::OutputStyle::KeepDeclarationOrder);\n\n        const auto short_name = \"default_parameters-\" + base_name + \".prm\";\n        prm.print_parameters(\n            short_name,\n            dealii::ParameterHandler::OutputStyle::Short |\n                dealii::ParameterHandler::OutputStyle::KeepDeclarationOrder\n\n        );\n      }\n      // all objects have to go out of scope, see\n      // https://github.com/dealii/dealii/issues/15111\n    }\n\n    dealii::ParameterAcceptor::clear();\n  }\n\n\n  /**\n   * A small Dispatch struct templated in Description that registers the\n   * callbacks.\n   */\n  template <typename Description, typename Number>\n  struct Dispatch {\n    Dispatch(const std::string &name)\n    {\n#ifdef DEBUG_OUTPUT\n      std::cout << \"Dispatch<Description, Number>::Dispatch() for »\" << name\n                << \"«\" << std::endl;\n#endif\n\n      EquationDispatch::register_create_parameter_files([name]() {\n        create_prm_files<Description, 1, Number>(name, false);\n        create_prm_files<Description, 2, Number>(name, true);\n        create_prm_files<Description, 3, Number>(name, false);\n      });\n\n      EquationDispatch::register_dispatch(\n          [name](const int dimension,\n                 const std::string &equation,\n                 const std::string &parameter_file,\n                 const MPI_Comm &mpi_comm,\n                 bool &time_loop_executed) {\n            if (equation != name)\n              return;\n\n            if (dealii::Utilities::MPI::this_mpi_process(mpi_comm) == 0) {\n              std::cout << \"[INFO] dispatching to driver »\" << equation\n                        << \"« with dim=\" << dimension << std::endl;\n            }\n\n            AssertThrow(time_loop_executed == false,\n                        dealii::ExcMessage(\n                            dave +\n                            \"Trying to execute more than one TimeLoop object \"\n                            \"with the given equation parameter »\" +\n                            equation + \"«\"));\n\n            if (dimension == 1) {\n              TimeLoop<Description, 1, Number> time_loop(mpi_comm);\n              dealii::ParameterAcceptor::initialize(parameter_file);\n              time_loop.run();\n              time_loop_executed = true;\n            } else if (dimension == 2) {\n              TimeLoop<Description, 2, Number> time_loop(mpi_comm);\n              dealii::ParameterAcceptor::initialize(parameter_file);\n              time_loop.run();\n              time_loop_executed = true;\n            } else if (dimension == 3) {\n              TimeLoop<Description, 3, Number> time_loop(mpi_comm);\n              dealii::ParameterAcceptor::initialize(parameter_file);\n              time_loop.run();\n              time_loop_executed = true;\n            }\n          });\n    }\n  };\n\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/CMakeLists.txt",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2022 - 2024 by the ryujin authors\n##\n\nadd_library(obj_euler OBJECT\n  equation_dispatch.cc\n  initial_state_library.cc\n  limiter.cc\n  riemann_solver.cc\n  )\nset_target_properties(obj_euler PROPERTIES LINKER_LANGUAGE CXX)\ndeal_ii_setup_target(obj_euler)\ntarget_link_libraries(obj_euler obj_common ${EXTERNAL_TARGETS})\n# Propagate the current source directory with PUBLIC visibility\ntarget_include_directories(obj_euler PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})\n"
  },
  {
    "path": "source/euler/Makefile",
    "content": "default: all\n.PHONY: default\n\n%:\n\t@cd .. && make $@\n.PHONY: %\n"
  },
  {
    "path": "source/euler/description.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"../stub_parabolic_module.h\"\n#include \"../stub_parabolic_system.h\"\n#include \"hyperbolic_system.h\"\n#include \"indicator.h\"\n#include \"limiter.h\"\n#include \"riemann_solver.h\"\n\nnamespace ryujin\n{\n  namespace Euler\n  {\n    /**\n     * A struct that contains all equation specific classes describing the\n     * chosen hyperbolic system, the indicator, the limiter and\n     * (approximate) Riemann solver.\n     *\n     * The compressible Euler equations of gas dynamics. Specialized\n     * implementation for a polytropic gas equation.\n     *\n     * The parabolic subsystem is chosen to be the identity.\n     *\n     * @ingroup EulerEquations\n     */\n    struct Description {\n      using HyperbolicSystem = Euler::HyperbolicSystem;\n\n      template <int dim, typename Number = double>\n      using HyperbolicSystemView = Euler::HyperbolicSystemView<dim, Number>;\n\n      using ParabolicSystem = ryujin::StubParabolicSystem;\n\n      template <int dim, typename Number = double>\n      using ParabolicModule =\n          ryujin::StubParabolicModule<Description, dim, Number>;\n\n      template <int dim, typename Number = double>\n      using Indicator = Euler::Indicator<dim, Number>;\n\n      template <int dim, typename Number = double>\n      using Limiter = Euler::Limiter<dim, Number>;\n\n      template <int dim, typename Number = double>\n      using RiemannSolver = Euler::RiemannSolver<dim, Number>;\n    };\n  } // namespace Euler\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/equation_dispatch.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2024 by the ryujin authors\n//\n\n#include \"description.h\"\n\n#include <compile_time_options.h>\n#include <equation_dispatch.h>\n\nnamespace ryujin\n{\n  namespace Euler\n  {\n    Dispatch<Description, NUMBER> dispatch_instance(\"euler\");\n  } // namespace Euler\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/hyperbolic_system.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <convenience_macros.h>\n#include <discretization.h>\n#include <loop.h>\n#include <multicomponent_vector.h>\n#include <patterns_conversion.h>\n#include <simd.h>\n#include <state_vector.h>\n\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/tensor.h>\n\n#include <array>\n\nnamespace ryujin\n{\n  namespace Euler\n  {\n    template <int dim, typename Number>\n    class HyperbolicSystemView;\n\n    /**\n     * The compressible Euler equations of gas dynamics. Specialized\n     * implementation for a polytropic gas equation.\n     *\n     * We have a (2 + dim) dimensional state space \\f$[\\rho, \\textbf m,\n     * E]\\f$, where \\f$\\rho\\f$ denotes the density, \\f$\\textbf m\\f$ is the\n     * momentum, and \\f$E\\f$ is the total energy.\n     *\n     * @ingroup EulerEquations\n     */\n    class HyperbolicSystem final : public dealii::ParameterAcceptor\n    {\n    public:\n      /**\n       * The name of the hyperbolic system as a string.\n       */\n      static inline const std::string problem_name =\n          \"Compressible Euler equations (polytropic gas EOS, optimized)\";\n\n      /**\n       * Constructor.\n       */\n      HyperbolicSystem(const std::string &subsection = \"/HyperbolicSystem\");\n\n      /**\n       * Return a view on the Hyperbolic System for a given dimension @p\n       * dim and choice of number type @p Number (which can be a scalar\n       * float, or double, as well as a VectorizedArray holding packed\n       * scalars.\n       */\n      template <int dim, typename Number>\n      auto view() const\n      {\n        return HyperbolicSystemView<dim, Number>{*this};\n      }\n\n      /**\n       * Part of step 1 of the hyperbolic update step: Compute \"precomputed\n       * values\" and fill into the state vector.\n       *\n       * @note The method does not update the ghost range of the state\n       * vector. The precomputed part has to be synchronized by explicitly\n       * calling the update ghost values function.\n       */\n      template <int dim, typename ScalarNumber>\n      void fill_precomputed_values(\n          const OfflineData<dim, ScalarNumber> &offline_data,\n          typename HyperbolicSystemView<dim, ScalarNumber>::StateVector\n              &state_vector,\n          const bool skip_constrained_dofs = true) const;\n\n    private:\n      /**\n       * @name Runtime parameters, internal fields, methods, and friends\n       */\n      //@{\n      double gamma_;\n\n      double reference_density_;\n      double vacuum_state_relaxation_small_;\n      double vacuum_state_relaxation_large_;\n\n      double gamma_inverse_;\n      double gamma_minus_one_inverse_;\n      double gamma_minus_one_over_gamma_plus_one_;\n      double gamma_plus_one_inverse_;\n\n      template <int dim, typename Number>\n      friend class HyperbolicSystemView;\n      //@}\n    }; /* HyperbolicSystem */\n\n\n    /**\n     * A view of the HyperbolicSystem that makes methods available for a\n     * given dimension @p dim and choice of number type @p Number (which\n     * can be a scalar float, or double, as well as a VectorizedArray\n     * holding packed scalars.\n     *\n     * Intended usage:\n     * ```\n     * HyperbolicSystem hyperbolic_system;\n     * const auto view = hyperbolic_system.template view<dim, Number>();\n     * const auto flux_i = view.flux_contribution(...);\n     * const auto flux_j = view.flux_contribution(...);\n     * const auto flux_ij = view.flux_divergence(flux_i, flux_j, c_ij);\n     * // etc.\n     * ```\n     */\n    template <int dim, typename Number>\n    class HyperbolicSystemView\n    {\n    public:\n      /**\n       * Constructor taking a reference to the underlying\n       * HyperbolicSystem\n       */\n      HyperbolicSystemView(const HyperbolicSystem &hyperbolic_system)\n          : hyperbolic_system_(hyperbolic_system)\n      {\n      }\n\n      /**\n       * Create a modified view from the current one:\n       */\n      template <int dim2, typename Number2>\n      auto view() const\n      {\n        return HyperbolicSystemView<dim2, Number2>{hyperbolic_system_};\n      }\n\n      /**\n       * The underlying scalar number type.\n       */\n      using ScalarNumber = typename get_value_type<Number>::type;\n\n      /**\n       * @name Access to runtime parameters\n       */\n      //@{\n\n      DEAL_II_ALWAYS_INLINE inline ScalarNumber gamma() const\n      {\n        return hyperbolic_system_.gamma_;\n      }\n\n      DEAL_II_ALWAYS_INLINE inline ScalarNumber reference_density() const\n      {\n        return hyperbolic_system_.reference_density_;\n      }\n\n      DEAL_II_ALWAYS_INLINE inline ScalarNumber\n      vacuum_state_relaxation_small() const\n      {\n        return hyperbolic_system_.vacuum_state_relaxation_small_;\n      }\n\n      DEAL_II_ALWAYS_INLINE inline ScalarNumber\n      vacuum_state_relaxation_large() const\n      {\n        return hyperbolic_system_.vacuum_state_relaxation_large_;\n      }\n\n      //@}\n      /**\n       * @name Access to cached inverses\n       *\n       * A collection of commonly used expressions with gamma that would\n       * otherwise need to be recomputed many times putting unnecessary\n       * pressure on the div/sqrt ALU unit.\n       */\n      //@{\n\n      DEAL_II_ALWAYS_INLINE inline ScalarNumber gamma_inverse() const\n      {\n        return ScalarNumber(hyperbolic_system_.gamma_inverse_);\n      }\n\n      DEAL_II_ALWAYS_INLINE inline ScalarNumber gamma_plus_one_inverse() const\n      {\n        return ScalarNumber(hyperbolic_system_.gamma_plus_one_inverse_);\n      }\n\n      DEAL_II_ALWAYS_INLINE inline ScalarNumber gamma_minus_one_inverse() const\n      {\n        return ScalarNumber(hyperbolic_system_.gamma_minus_one_inverse_);\n      }\n\n      DEAL_II_ALWAYS_INLINE inline ScalarNumber\n      gamma_minus_one_over_gamma_plus_one() const\n      {\n        return ScalarNumber(\n            hyperbolic_system_.gamma_minus_one_over_gamma_plus_one_);\n      }\n\n      //@}\n      /**\n       * constexpr booleans used in the EulerInitialStates namespace\n       */\n      //@{\n\n      static constexpr bool have_gamma = true;\n      static constexpr bool have_eos_interpolation_b = false;\n      static constexpr bool have_energy_equation = true;\n\n      //@}\n      /**\n       * @name Internal data\n       */\n      //@{\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n    public:\n      //@}\n      /**\n       * @name Types and constexpr constants\n       */\n      //@{\n\n      /**\n       * The dimension of the state space.\n       */\n      static constexpr unsigned int problem_dimension = 2 + dim;\n\n      /**\n       * Storage type for a (conserved) state vector \\f$\\boldsymbol U\\f$.\n       */\n      using state_type = dealii::Tensor<1, problem_dimension, Number>;\n\n      /**\n       * Storage type for the flux \\f$\\mathbf{f}\\f$.\n       */\n      using flux_type =\n          dealii::Tensor<1, problem_dimension, dealii::Tensor<1, dim, Number>>;\n\n      /**\n       * The storage type used for flux contributions.\n       */\n      using flux_contribution_type = flux_type;\n\n      /**\n       * An array holding all component names of the conserved state as a\n       * string.\n       */\n      static inline const auto component_names =\n          []() -> std::array<std::string, problem_dimension> {\n        if constexpr (dim == 1)\n          return {\"rho\", \"m\", \"E\"};\n        else if constexpr (dim == 2)\n          return {\"rho\", \"m_1\", \"m_2\", \"E\"};\n        else if constexpr (dim == 3)\n          return {\"rho\", \"m_1\", \"m_2\", \"m_3\", \"E\"};\n        __builtin_trap();\n      }();\n\n      /**\n       * An array holding all component names of the primitive state as a\n       * string.\n       */\n      static inline const auto primitive_component_names =\n          []() -> std::array<std::string, problem_dimension> {\n        if constexpr (dim == 1)\n          return {\"rho\", \"v\", \"p\"};\n        else if constexpr (dim == 2)\n          return {\"rho\", \"v_1\", \"v_2\", \"p\"};\n        else if constexpr (dim == 3)\n          return {\"rho\", \"v_1\", \"v_2\", \"v_3\", \"p\"};\n        __builtin_trap();\n      }();\n\n      /**\n       * The number of precomputed values.\n       */\n      static constexpr unsigned int n_precomputed_values = 2;\n\n      /**\n       * Array type used for precomputed values.\n       */\n      using precomputed_type = std::array<Number, n_precomputed_values>;\n\n      /**\n       * An array holding all component names of the precomputed values.\n       */\n      static inline const auto precomputed_names =\n          std::array<std::string, n_precomputed_values>{\"s\", \"eta_h\"};\n\n      /**\n       * The number of precomputed initial values.\n       */\n      static constexpr unsigned int n_initial_precomputed_values = 0;\n\n      /**\n       * Array type used for precomputed initial values.\n       */\n      using initial_precomputed_type =\n          std::array<Number, n_initial_precomputed_values>;\n\n      /**\n       * An array holding all component names of the precomputed values.\n       */\n      static inline const auto initial_precomputed_names =\n          std::array<std::string, n_initial_precomputed_values>{};\n\n      /**\n       * A compound state vector.\n       */\n      using StateVector = Vectors::\n          StateVector<ScalarNumber, problem_dimension, n_precomputed_values>;\n\n      /**\n       * MulticomponentVector for storing the hyperbolic state vector:\n       */\n      using HyperbolicVector =\n          Vectors::MultiComponentVector<ScalarNumber, problem_dimension>;\n\n      /**\n       * MulticomponentVector for storing a vector of precomputed states:\n       */\n      using PrecomputedVector =\n          Vectors::MultiComponentVector<ScalarNumber, n_precomputed_values>;\n\n      /**\n       * MulticomponentVector for storing a vector of precomputed initial\n       * states:\n       */\n      using InitialPrecomputedVector =\n          Vectors::MultiComponentVector<ScalarNumber,\n                                        n_initial_precomputed_values>;\n\n      //@}\n      /**\n       * @name Computing derived physical quantities\n       */\n      //@{\n\n      /**\n       * For a given (2+dim dimensional) state vector <code>U</code>, return\n       * the density <code>U[0]</code>\n       */\n      static Number density(const state_type &U);\n\n      /**\n       * Given a density @p rho this function returns 0 if the magniatude\n       * of rho is smaller or equal than relaxation_large * rho_cutoff.\n       * Otherwise rho is returned unmodified. Here, rho_cutoff is the\n       * reference density multiplied by eps.\n       */\n      Number filter_vacuum_density(const Number &rho) const;\n\n      /**\n       * For a given (2+dim dimensional) state vector <code>U</code>, return\n       * the momentum vector <code>[U[1], ..., U[1+dim]]</code>.\n       */\n      static dealii::Tensor<1, dim, Number> momentum(const state_type &U);\n\n      /**\n       * For a given (2+dim dimensional) state vector <code>U</code>, return\n       * the total energy <code>U[1+dim]</code>\n       */\n      static Number total_energy(const state_type &U);\n\n      /**\n       * For a given (2+dim dimensional) state vector <code>U</code>, compute\n       * and return the internal energy \\f$\\varepsilon = (\\rho e)\\f$.\n       */\n      static Number internal_energy(const state_type &U);\n\n      /**\n       * For a given (2+dim dimensional) state vector <code>U</code>, compute\n       * and return the derivative of the internal energy\n       * \\f$\\varepsilon = (\\rho e)\\f$.\n       */\n      static state_type internal_energy_derivative(const state_type &U);\n\n      /**\n       * For a given (2+dim dimensional) state vector <code>U</code>, compute\n       * and return the pressure \\f$p\\f$.\n       *\n       * We assume that the pressure is given by a polytropic equation of\n       * state, i.e.,\n       * \\f[\n       *   p = (\\gamma - 1)\\;(\\rho e)\n       * \\f]\n       */\n      Number pressure(const state_type &U) const;\n\n      /**\n       * For a given (2+dim dimensional) state vector <code>U</code>, compute\n       * the (physical) speed of sound:\n       * \\f[\n       *   c^2 = \\frac{\\gamma\\,p}{\\rho}\n       * \\f]\n       */\n      Number speed_of_sound(const state_type &U) const;\n\n      /**\n       * For a given (2+dim dimensional) state vector <code>U</code>, compute\n       * and return the (scaled) specific entropy\n       * \\f[\n       *   e^{(\\gamma-1)s} = \\frac{\\rho\\,e}{\\rho^\\gamma}.\n       * \\f]\n       */\n      Number specific_entropy(const state_type &U) const;\n\n      /**\n       * For a given (2+dim dimensional) state vector <code>U</code>, compute\n       * and return the Harten-type entropy\n       * \\f[\n       *   \\eta = (\\rho^2 e) ^ {1 / (\\gamma + 1)}.\n       * \\f]\n       */\n      Number harten_entropy(const state_type &U) const;\n\n      /**\n       * For a given (2+dim dimensional) state vector <code>U</code>, compute\n       * and return the derivative \\f$\\eta'\\f$ of the Harten-type entropy\n       * \\f[\n       *   \\eta = (\\rho^2 e) ^ {1 / (\\gamma + 1)}.\n       * \\f]\n       */\n      state_type harten_entropy_derivative(const state_type &U) const;\n\n      /**\n       * For a given (2+dim dimensional) state vector <code>U</code>, compute\n       * and return the entropy \\f$\\eta = p^{1/\\gamma}\\f$.\n       */\n      Number mathematical_entropy(const state_type &U) const;\n\n      /**\n       * For a given (2+dim dimensional) state vector <code>U</code>, compute\n       * and return the derivative \\f$\\eta'\\f$ of the entropy \\f$\\eta =\n       * p^{1/\\gamma}\\f$.\n       */\n      state_type mathematical_entropy_derivative(const state_type &U) const;\n\n      /**\n       * Returns whether the state @p U is admissible. If @p U is a\n       * vectorized state then @p U is admissible if all vectorized values\n       * are admissible.\n       */\n      bool is_admissible(const state_type &U) const;\n\n      //@}\n      /**\n       * @name Special functions for boundary states\n       */\n      //@{\n\n      /**\n       * For a given state @p U and normal direction @p normal returns the\n       * n-th pair of left and right eigenvectors of the linearized normal\n       * flux.\n       */\n      template <int component>\n      std::array<state_type, 2> linearized_eigenvector(\n          const state_type &U,\n          const dealii::Tensor<1, dim, Number> &normal) const;\n\n      /**\n       * Decomposes a given state @p U into Riemann invariants and then\n       * replaces the first or second Riemann characteristic from the one\n       * taken from @p U_bar state. Note that the @p U_bar state is just the\n       * prescribed dirichlet values.\n       */\n      template <int component>\n      state_type prescribe_riemann_characteristic(\n          const state_type &U,\n          const state_type &U_bar,\n          const dealii::Tensor<1, dim, Number> &normal) const;\n\n      /**\n       * Apply boundary conditions.\n       *\n       * For the compressible Euler equations we have:\n       *\n       *  - Dirichlet boundary conditions by prescribing the return value of\n       *    get_dirichlet_data() as is.\n       *\n       *  - Slip boundary conditions where we remove the normal component of\n       *    the momentum.\n       *\n       *  - No slip boundary conditions where we set the momentum to 0.\n       *\n       *  - \"Dynamic boundary\" conditions that prescribe different Riemann\n       *    invariants from the return value of get_dirichlet_data()\n       *    depending on the flow state (supersonic versus subsonic, outflow\n       *    versus inflow).\n       */\n      template <typename Lambda>\n      state_type\n      apply_boundary_conditions(const dealii::types::boundary_id id,\n                                const state_type &U,\n                                const dealii::Tensor<1, dim, Number> &normal,\n                                const Lambda &get_dirichlet_data) const;\n\n      //@}\n      /**\n       * @name Flux computations\n       */\n      //@{\n\n      /**\n       * Given a state @p U compute the flux\n       * \\f[\n       * \\begin{pmatrix}\n       *   \\textbf m \\\\\n       *   \\textbf v\\otimes \\textbf m + p\\mathbb{I}_d \\\\\n       *   \\textbf v(E+p)\n       * \\end{pmatrix},\n       * \\f]\n       */\n      flux_type f(const state_type &U) const;\n\n      /**\n       * Given a state @p U_i and an index @p i compute flux contributions.\n       *\n       * Intended usage:\n       * ```\n       * Indicator<dim, Number> indicator;\n       * for (unsigned int i = n_internal; i < n_owned; ++i) {\n       *   // ...\n       *   const auto flux_i = flux_contribution(precomputed..., i, U_i);\n       *   for (unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {\n       *     // ...\n       *     const auto flux_j = flux_contribution(precomputed..., js, U_j);\n       *     const auto flux_ij = flux_divergence(flux_i, flux_j, c_ij);\n       *   }\n       * }\n       * ```\n       *\n       * For the Euler equations we simply compute <code>f(U_i)</code>.\n       */\n      flux_contribution_type\n      flux_contribution(const PrecomputedVector &pv,\n                        const InitialPrecomputedVector &ipv,\n                        const unsigned int i,\n                        const state_type &U_i) const;\n\n      flux_contribution_type\n      flux_contribution(const PrecomputedVector &pv,\n                        const InitialPrecomputedVector &ipv,\n                        const unsigned int *js,\n                        const state_type &U_j) const;\n\n      /**\n       * Given flux contributions @p flux_i and @p flux_j compute the flux\n       * <code>(-f(U_i) - f(U_j) * c_ij</code>\n       */\n      state_type\n      flux_divergence(const flux_contribution_type &flux_i,\n                      const flux_contribution_type &flux_j,\n                      const dealii::Tensor<1, dim, Number> &c_ij) const;\n\n      /** The low-order and high-order fluxes are the same */\n      static constexpr bool have_high_order_flux = false;\n\n      state_type high_order_flux_divergence(\n          const flux_contribution_type &flux_i,\n          const flux_contribution_type &flux_j,\n          const dealii::Tensor<1, dim, Number> &c_ij) const = delete;\n\n      //@}\n      /**\n       * @name Computing stencil source terms\n       */\n      //@{\n\n      /** We do not have source terms: */\n      static constexpr bool have_source_terms = false;\n\n      state_type nodal_source(const PrecomputedVector &pv,\n                              const unsigned int i,\n                              const state_type &U_i,\n                              const ScalarNumber tau) const = delete;\n\n      state_type nodal_source(const PrecomputedVector &pv,\n                              const unsigned int *js,\n                              const state_type &U_j,\n                              const ScalarNumber tau) const = delete;\n\n      //@}\n      /**\n       * @name State transformations\n       */\n      //@{\n\n      /**\n       * Given a state vector associated with a different spatial\n       * dimensions than the current one, return an \"expanded\" version of\n       * the state vector associated with @a dim spatial dimensions where\n       * the momentum vector of the conserved state @p state is expaned\n       * with zeros to a total length of @a dim entries.\n       *\n       * @note @a dim has to be larger or equal than the dimension of the\n       * @a ST vector.\n       */\n      template <typename ST>\n      state_type expand_state(const ST &state) const;\n\n      /**\n       * Given an initial state [rho, u_1, ..., u_d, p] return a\n       * conserved state [rho, m_1, ..., m_d, E].\n       *\n       * This function simply calls from_primitive_state() and\n       * expand_state().\n       *\n       * @note This function is used to conveniently convert (user\n       * provided) primitive initial states with pressure values to a\n       * conserved state in the EulerInitialStateLibrary. As such, this\n       * function is implemented in the Euler::HyperbolicSystem and\n       * EulerAEOS::HyperbolicSystem classes.\n       */\n      template <typename ST>\n      state_type from_initial_state(const ST &initial_state) const;\n\n      /**\n       * Given a primitive state [rho, u_1, ..., u_d, p] return a conserved\n       * state\n       */\n      state_type from_primitive_state(const state_type &primitive_state) const;\n\n      /**\n       * Given a conserved state return a primitive state [rho, u_1, ..., u_d,\n       * p]\n       */\n      state_type to_primitive_state(const state_type &state) const;\n\n      /**\n       * Transform the current state according to a  given operator\n       * @p lambda acting on a @a dim dimensional momentum (or velocity)\n       * vector.\n       */\n      template <typename Lambda>\n      state_type apply_galilei_transform(const state_type &state,\n                                         const Lambda &lambda) const;\n      //@}\n    }; /* HyperbolicSystemView */\n\n\n    /*\n     * -------------------------------------------------------------------------\n     * Inline definitions\n     * -------------------------------------------------------------------------\n     */\n\n    inline HyperbolicSystem::HyperbolicSystem(const std::string &subsection)\n        : ParameterAcceptor(subsection)\n    {\n      gamma_ = 7. / 5.;\n      add_parameter(\"gamma\", gamma_, \"The ratio of specific heats\");\n\n      reference_density_ = 1.;\n      add_parameter(\"reference density\",\n                    reference_density_,\n                    \"Problem specific density reference\");\n\n      vacuum_state_relaxation_small_ = 1.e2;\n      add_parameter(\"vacuum state relaxation small\",\n                    vacuum_state_relaxation_small_,\n                    \"Problem specific vacuum relaxation parameter\");\n\n      vacuum_state_relaxation_large_ = 1.e4;\n      add_parameter(\"vacuum state relaxation large\",\n                    vacuum_state_relaxation_large_,\n                    \"Problem specific vacuum relaxation parameter\");\n\n      /*\n       * Precompute a number of derived gamma coefficients that contain\n       * divisions:\n       */\n      const auto compute_inverses = [this] {\n        gamma_inverse_ = 1. / gamma_;\n        gamma_plus_one_inverse_ = 1. / (gamma_ + 1.);\n        gamma_minus_one_inverse_ = 1. / (gamma_ - 1.);\n        gamma_minus_one_over_gamma_plus_one_ = (gamma_ - 1.) / (gamma_ + 1.);\n      };\n\n      compute_inverses();\n      ParameterAcceptor::parse_parameters_call_back.connect(compute_inverses);\n    }\n\n\n    template <int dim, typename ScalarNumber>\n    inline void HyperbolicSystem::fill_precomputed_values(\n        const OfflineData<dim, ScalarNumber> &offline_data,\n        typename HyperbolicSystemView<dim, ScalarNumber>::StateVector\n            &state_vector,\n        const bool skip_constrained_dofs) const\n    {\n      const unsigned int n_internal = offline_data.n_locally_internal();\n      const unsigned int n_owned = offline_data.n_locally_owned();\n      const auto &sparsity_simd = offline_data.sparsity_pattern_simd();\n      using VA = dealii::VectorizedArray<ScalarNumber>;\n\n      const auto &U = std::get<0>(state_vector);\n      auto &precomputed = std::get<1>(state_vector);\n\n      const auto body = [&](auto sentinel, unsigned int i) {\n        using T = decltype(sentinel);\n        using View = HyperbolicSystemView<dim, T>;\n        using precomputed_type = typename View::precomputed_type;\n\n        const unsigned int row_length = sparsity_simd.row_length(i);\n        if (skip_constrained_dofs && row_length == 1)\n          return;\n\n        const auto U_i = U.template read_tensor<T>(i);\n        const auto view = this->view<dim, T>();\n        const precomputed_type prec_i{view.specific_entropy(U_i),\n                                      view.harten_entropy(U_i)};\n        precomputed.template write_tensor<T>(prec_i, i);\n      };\n\n      cpu_simd_loop<ScalarNumber>(\"time_step_1\", body, 0, n_internal, n_owned);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::density(const state_type &U)\n    {\n      return U[0];\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::filter_vacuum_density(\n        const Number &rho) const\n    {\n      constexpr ScalarNumber eps = std::numeric_limits<ScalarNumber>::epsilon();\n      const Number rho_cutoff_large =\n          reference_density() * vacuum_state_relaxation_large() * eps;\n\n      return dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(\n          std::abs(rho), rho_cutoff_large, Number(0.), rho);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline dealii::Tensor<1, dim, Number>\n    HyperbolicSystemView<dim, Number>::momentum(const state_type &U)\n    {\n      dealii::Tensor<1, dim, Number> result;\n      for (unsigned int i = 0; i < dim; ++i)\n        result[i] = U[1 + i];\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::total_energy(const state_type &U)\n    {\n      return U[1 + dim];\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::internal_energy(const state_type &U)\n    {\n      /*\n       * rho e = (E - 1/2*m^2/rho)\n       */\n      const Number rho_inverse = ScalarNumber(1.) / density(U);\n      const auto m = momentum(U);\n      const Number E = total_energy(U);\n      return E - ScalarNumber(0.5) * m.norm_square() * rho_inverse;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::internal_energy_derivative(\n        const state_type &U) -> state_type\n    {\n      /*\n       * With\n       *   rho e = E - 1/2 |m|^2 / rho\n       * we get\n       *   (rho e)' = (1/2m^2/rho^2, -m/rho , 1 )^T\n       */\n\n      const Number rho_inverse = ScalarNumber(1.) / density(U);\n      const auto u = momentum(U) * rho_inverse;\n\n      state_type result;\n\n      result[0] = ScalarNumber(0.5) * u.norm_square();\n      for (unsigned int i = 0; i < dim; ++i) {\n        result[1 + i] = -u[i];\n      }\n      result[dim + 1] = ScalarNumber(1.);\n\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::pressure(const state_type &U) const\n    {\n      /* p = (gamma - 1) * (rho e) */\n      return (gamma() - ScalarNumber(1.)) * internal_energy(U);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::speed_of_sound(const state_type &U) const\n    {\n      /* c^2 = gamma * p / rho */\n      const Number rho_inverse = ScalarNumber(1.) / density(U);\n      const Number p = pressure(U);\n      return std::sqrt(gamma() * p * rho_inverse);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::specific_entropy(\n        const state_type &U) const\n    {\n      /* exp((gamma - 1)s) = (rho e) / rho ^ gamma */\n      const auto rho_inverse = ScalarNumber(1.) / density(U);\n      return internal_energy(U) * ryujin::pow(rho_inverse, gamma());\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::harten_entropy(const state_type &U) const\n    {\n      /* rho^2 e = \\rho E - 1/2*m^2 */\n\n      const Number rho = density(U);\n      const auto m = momentum(U);\n      const Number E = total_energy(U);\n\n      const Number rho_rho_e = rho * E - ScalarNumber(0.5) * m.norm_square();\n      return ryujin::pow(rho_rho_e, gamma_plus_one_inverse());\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::harten_entropy_derivative(\n        const state_type &U) const -> state_type\n    {\n      /*\n       * With\n       *   eta = (rho^2 e) ^ 1/(gamma+1)\n       *   rho^2 e = rho * E - 1/2 |m|^2\n       *\n       * we get\n       *\n       *   eta' = 1/(gamma+1) * (rho^2 e) ^ -gamma/(gamma+1) * (E,-m,rho)^T\n       *\n       */\n\n      const Number rho = density(U);\n      const auto m = momentum(U);\n      const Number E = total_energy(U);\n\n      const Number rho_rho_e = rho * E - ScalarNumber(0.5) * m.norm_square();\n\n      const auto factor =\n          gamma_plus_one_inverse() *\n          ryujin::pow(rho_rho_e, -gamma() * gamma_plus_one_inverse());\n\n      state_type result;\n\n      result[0] = factor * E;\n      for (unsigned int i = 0; i < dim; ++i)\n        result[1 + i] = -factor * m[i];\n      result[dim + 1] = factor * rho;\n\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::mathematical_entropy(\n        const state_type &U) const\n    {\n      using ScalarNumber = typename get_value_type<Number>::type;\n      const auto p = pressure(U);\n      return ryujin::pow(p, gamma_inverse());\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::mathematical_entropy_derivative(\n        const state_type &U) const -> state_type\n    {\n      /*\n       * With\n       *   eta = p ^ (1/gamma)\n       *   p = (gamma - 1) * (rho e)\n       *   rho e = E - 1/2 |m|^2 / rho\n       *\n       * we get\n       *\n       *   eta' = (gamma - 1)/gamma p ^(1/gamma - 1) *\n       *\n       *     (1/2m^2/rho^2 , -m/rho , 1 )^T\n       */\n      const Number rho = density(U);\n      const Number rho_inverse = ScalarNumber(1.) / rho;\n      const auto u = momentum(U) * rho_inverse;\n      const auto p = pressure(U);\n\n      const auto factor = (gamma() - ScalarNumber(1.0)) * gamma_inverse() *\n                          ryujin::pow(p, gamma_inverse() - ScalarNumber(1.));\n\n      state_type result;\n\n      result[0] = factor * ScalarNumber(0.5) * u.norm_square();\n      result[dim + 1] = factor;\n      for (unsigned int i = 0; i < dim; ++i) {\n        result[1 + i] = -factor * u[i];\n      }\n\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline bool\n    HyperbolicSystemView<dim, Number>::is_admissible(const state_type &U) const\n    {\n      const auto rho_new = density(U);\n      const auto e_new = internal_energy(U);\n      const auto s_new = specific_entropy(U);\n\n      constexpr auto gt = dealii::SIMDComparison::greater_than;\n      using T = Number;\n      const auto test =\n          dealii::compare_and_apply_mask<gt>(rho_new, T(0.), T(0.), T(-1.)) + //\n          dealii::compare_and_apply_mask<gt>(e_new, T(0.), T(0.), T(-1.)) +   //\n          dealii::compare_and_apply_mask<gt>(s_new, T(0.), T(0.), T(-1.));\n\n#ifdef DEBUG_OUTPUT\n      if (!(test == Number(0.))) {\n        std::cout << std::fixed << std::setprecision(16);\n        std::cout << \"Bounds violation: Negative state [rho, e, s] detected!\\n\";\n        std::cout << \"\\t\\trho: \" << rho_new << \"\\n\";\n        std::cout << \"\\t\\tint: \" << e_new << \"\\n\";\n        std::cout << \"\\t\\tent: \" << s_new << \"\\n\" << std::endl;\n      }\n#endif\n\n      return (test == Number(0.));\n    }\n\n\n    template <int dim, typename Number>\n    template <int component>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::linearized_eigenvector(\n        const state_type &U, const dealii::Tensor<1, dim, Number> &normal) const\n        -> std::array<state_type, 2>\n    {\n      static_assert(component == 1 || component == problem_dimension,\n                    \"Only first and last eigenvectors implemented\");\n\n      const auto rho = density(U);\n      const auto m = momentum(U);\n      const auto v = m / rho;\n      const auto a = speed_of_sound(U);\n      const auto gamma = this->gamma();\n\n      state_type b;\n      state_type c;\n\n      const auto e_k = 0.5 * v.norm_square();\n\n      switch (component) {\n      case 1:\n        b[0] = (gamma - 1.) * e_k + a * v * normal;\n        for (unsigned int i = 0; i < dim; ++i)\n          b[1 + i] = (1. - gamma) * v[i] - a * normal[i];\n        b[dim + 1] = gamma - 1.;\n        b /= 2. * a * a;\n\n        c[0] = 1.;\n        for (unsigned int i = 0; i < dim; ++i)\n          c[1 + i] = v[i] - a * normal[i];\n        c[dim + 1] = a * a / (gamma - 1) + e_k - a * (v * normal);\n\n        return {b, c};\n\n      case problem_dimension:\n        b[0] = (gamma - 1.) * e_k - a * v * normal;\n        for (unsigned int i = 0; i < dim; ++i)\n          b[1 + i] = (1. - gamma) * v[i] + a * normal[i];\n        b[dim + 1] = gamma - 1.;\n        b /= 2. * a * a;\n\n        c[0] = 1.;\n        for (unsigned int i = 0; i < dim; ++i)\n          c[1 + i] = v[i] + a * normal[i];\n        c[dim + 1] = a * a / (gamma - 1) + e_k + a * (v * normal);\n\n        return {b, c};\n      }\n\n      __builtin_unreachable();\n    }\n\n\n    template <int dim, typename Number>\n    template <int component>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::prescribe_riemann_characteristic(\n        const state_type &U,\n        const state_type &U_bar,\n        const dealii::Tensor<1, dim, Number> &normal) const -> state_type\n    {\n      static_assert(component == 1 || component == 2,\n                    \"component has to be 1 or 2\");\n\n      const auto m = momentum(U);\n      const auto rho = density(U);\n      const auto a = speed_of_sound(U);\n      const auto vn = m * normal / rho;\n\n      const auto m_bar = momentum(U_bar);\n      const auto rho_bar = density(U_bar);\n      const auto a_bar = speed_of_sound(U_bar);\n      const auto vn_bar = m_bar * normal / rho_bar;\n\n      /* First Riemann characteristic: v* n - 2 / (gamma - 1) * a */\n\n      const auto R_1 = component == 1\n                           ? vn_bar - 2. * a_bar / (gamma() - ScalarNumber(1.))\n                           : vn - 2. * a / (gamma() - ScalarNumber(1.));\n\n      /* Second Riemann characteristic: v* n + 2 / (gamma() - 1) * a */\n\n      const auto R_2 = component == 2\n                           ? vn_bar + 2. * a_bar / (gamma() - ScalarNumber(1.))\n                           : vn + 2. * a / (gamma() - ScalarNumber(1.));\n\n      const auto p = pressure(U);\n      const auto s = p / ryujin::pow(rho, gamma());\n\n      const auto vperp = m / rho - vn * normal;\n\n      const auto vn_new = 0.5 * (R_1 + R_2);\n\n      auto rho_new = 1. / (gamma() * s) *\n                     ryujin::fixed_power<2>(ScalarNumber((gamma() - 1.) / 4.) *\n                                            (R_2 - R_1));\n      rho_new = ryujin::pow(rho_new, 1. / (gamma() - 1.));\n\n      const auto p_new = s * std::pow(rho_new, gamma());\n\n      state_type U_new;\n      U_new[0] = rho_new;\n      for (unsigned int d = 0; d < dim; ++d) {\n        U_new[1 + d] = rho_new * (vn_new * normal + vperp)[d];\n      }\n      U_new[1 + dim] = p_new / ScalarNumber(gamma() - 1.) +\n                       0.5 * rho_new * (vn_new * vn_new + vperp.norm_square());\n\n      return U_new;\n    }\n\n\n    template <int dim, typename Number>\n    template <typename Lambda>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::apply_boundary_conditions(\n        dealii::types::boundary_id id,\n        const state_type &U,\n        const dealii::Tensor<1, dim, Number> &normal,\n        const Lambda &get_dirichlet_data) const -> state_type\n    {\n      state_type result = U;\n\n      if (id == Boundary::dirichlet) {\n        result = get_dirichlet_data();\n\n      } else if (id == Boundary::dirichlet_momentum) {\n        /*\n         * Only enforce Dirichlet conditions on the momentum, and keep the\n         * internal energy constant:\n         */\n        const auto m_dirichlet = momentum(get_dirichlet_data());\n        const auto rho = density(result);\n        const auto m = momentum(result);\n\n        for (unsigned int k = 0; k < dim; ++k)\n          result[k + 1] = m_dirichlet[k];\n        result[dim + 1] +=\n            Number(0.5) / rho * (m_dirichlet.norm_square() - m.norm_square());\n\n      } else if (id == Boundary::dirichlet_velocity) {\n        /*\n         * Only enforce Dirichlet conditions on the velocity, and keep the\n         * internal energy constant:\n         */\n        const auto U_dirichlet = get_dirichlet_data();\n        const auto rho_dirichlet = density(U_dirichlet);\n        const auto v_dirichlet = momentum(U_dirichlet) / rho_dirichlet;\n        const auto rho = density(result);\n        const auto v = momentum(result) / rho;\n\n        for (unsigned int k = 0; k < dim; ++k)\n          result[k + 1] = rho * v_dirichlet[k];\n        result[dim + 1] +=\n            Number(0.5) * rho * (v_dirichlet.norm_square() - v.norm_square());\n\n      } else if (id == Boundary::slip) {\n        auto m = momentum(U);\n        m -= 1. * (m * normal) * normal;\n        for (unsigned int k = 0; k < dim; ++k)\n          result[k + 1] = m[k];\n\n      } else if (id == Boundary::no_slip) {\n        for (unsigned int k = 0; k < dim; ++k)\n          result[k + 1] = Number(0.);\n\n      } else if (id == Boundary::dynamic) {\n        /*\n         * On dynamic boundary conditions, we distinguish four cases:\n         *\n         *  - supersonic inflow: prescribe full state\n         *  - subsonic inflow:\n         *      decompose into Riemann invariants and leave R_2\n         *      characteristic untouched.\n         *  - supersonic outflow: do nothing\n         *  - subsonic outflow:\n         *      decompose into Riemann invariants and prescribe incoming\n         *      R_1 characteristic.\n         */\n        const auto m = momentum(U);\n        const auto rho = density(U);\n        const auto a = speed_of_sound(U);\n        const auto vn = m * normal / rho;\n\n        /* Supersonic inflow: */\n        if (vn < -a) {\n          result = get_dirichlet_data();\n        }\n\n        /* Subsonic inflow: */\n        if (vn >= -a && vn <= 0.) {\n          const auto U_dirichlet = get_dirichlet_data();\n          result = prescribe_riemann_characteristic<2>(U_dirichlet, U, normal);\n        }\n\n        /* Subsonic outflow: */\n        if (vn > 0. && vn <= a) {\n          const auto U_dirichlet = get_dirichlet_data();\n          result = prescribe_riemann_characteristic<1>(U, U_dirichlet, normal);\n        }\n\n        /* Supersonic outflow: do nothing, i.e., keep U as is */\n\n      } else {\n        AssertThrow(false, dealii::ExcNotImplemented());\n      }\n\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::f(const state_type &U) const -> flux_type\n    {\n      const auto rho_inverse = ScalarNumber(1.) / density(U);\n      const auto m = momentum(U);\n      const auto p = pressure(U);\n      const auto E = total_energy(U);\n\n      flux_type result;\n\n      result[0] = m;\n      for (unsigned int i = 0; i < dim; ++i) {\n        result[1 + i] = m * (m[i] * rho_inverse);\n        result[1 + i][i] += p;\n      }\n      result[dim + 1] = m * (rho_inverse * (E + p));\n\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::flux_contribution(\n        const PrecomputedVector & /*pv*/,\n        const InitialPrecomputedVector & /*ipv*/,\n        const unsigned int /*i*/,\n        const state_type &U_i) const -> flux_contribution_type\n    {\n      return f(U_i);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::flux_contribution(\n        const PrecomputedVector & /*pv*/,\n        const InitialPrecomputedVector & /*ipv*/,\n        const unsigned int * /*js*/,\n        const state_type &U_j) const -> flux_contribution_type\n    {\n      return f(U_j);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::flux_divergence(\n        const flux_contribution_type &flux_i,\n        const flux_contribution_type &flux_j,\n        const dealii::Tensor<1, dim, Number> &c_ij) const -> state_type\n    {\n      return -contract(add(flux_i, flux_j), c_ij);\n    }\n\n\n    template <int dim, typename Number>\n    template <typename ST>\n    auto HyperbolicSystemView<dim, Number>::expand_state(const ST &state) const\n        -> state_type\n    {\n      using T = typename ST::value_type;\n      static_assert(std::is_same_v<Number, T>, \"template mismatch\");\n\n      constexpr auto dim2 = ST::dimension - 2;\n      static_assert(dim >= dim2,\n                    \"the space dimension of the argument state must not be \"\n                    \"larger than the one of the target state\");\n\n      state_type result;\n      result[0] = state[0];\n      result[dim + 1] = state[dim2 + 1];\n      for (unsigned int i = 1; i < dim2 + 1; ++i)\n        result[i] = state[i];\n\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    template <typename ST>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::from_initial_state(\n        const ST &initial_state) const -> state_type\n    {\n      const auto primitive_state = expand_state(initial_state);\n      return from_primitive_state(primitive_state);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::from_primitive_state(\n        const state_type &primitive_state) const -> state_type\n    {\n      const auto &rho = primitive_state[0];\n      /* extract velocity: */\n      const auto u = /*SIC!*/ momentum(primitive_state);\n      const auto &p = primitive_state[dim + 1];\n\n      auto state = primitive_state;\n      /* Fix up momentum: */\n      for (unsigned int i = 1; i < dim + 1; ++i)\n        state[i] *= rho;\n      /* Compute total energy: */\n      state[dim + 1] =\n          p / (ScalarNumber(gamma() - 1.)) + Number(0.5) * rho * u * u;\n\n      return state;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::to_primitive_state(\n        const state_type &state) const -> state_type\n    {\n      const auto &rho = state[0];\n      const auto rho_inverse = Number(1.) / rho;\n      const auto p = pressure(state);\n\n      auto primitive_state = state;\n      /* Fix up velocity: */\n      for (unsigned int i = 1; i < dim + 1; ++i)\n        primitive_state[i] *= rho_inverse;\n      /* Set pressure: */\n      primitive_state[dim + 1] = p;\n\n      return primitive_state;\n    }\n\n\n    template <int dim, typename Number>\n    template <typename Lambda>\n    auto HyperbolicSystemView<dim, Number>::apply_galilei_transform(\n        const state_type &state, const Lambda &lambda) const -> state_type\n    {\n      auto result = state;\n      const auto M = lambda(momentum(state));\n      for (unsigned int d = 0; d < dim; ++d)\n        result[1 + d] = M[d];\n      return result;\n    }\n\n  } // namespace Euler\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/indicator.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"hyperbolic_system.h\"\n\n#include <multicomponent_vector.h>\n#include <simd.h>\n\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/vectorization.h>\n\nnamespace ryujin\n{\n  namespace Euler\n  {\n    template <typename ScalarNumber = double>\n    class IndicatorParameters : public dealii::ParameterAcceptor\n    {\n    public:\n      IndicatorParameters(const std::string &subsection = \"/Indicator\")\n          : ParameterAcceptor(subsection)\n      {\n        evc_factor_ = ScalarNumber(1.);\n        add_parameter(\"evc factor\",\n                      evc_factor_,\n                      \"Factor for scaling the entropy viscocity commuator\");\n      }\n\n      ACCESSOR_READ_ONLY(evc_factor);\n\n    private:\n      ScalarNumber evc_factor_;\n    };\n\n\n    /**\n     * This class implements an indicator strategy used to form the\n     * preliminary high-order update.\n     *\n     * The indicator is an entropy-viscosity commutator as described\n     * in @cite GuermondEtAl2011 and @cite GuermondEtAl2018. For a given\n     * entropy \\f$\\eta\\f$ (either the mathematical entropy, or a Harten\n     * entropy, see the documentation of HyperbolicSystem) we let\n     * \\f$\\eta'\\f$ denote its derivative with respect to the state variables.\n     * We then compute a normalized entropy viscosity ratio \\f$\\alpha_i^n\\f$\n     * for the state \\f$\\boldsymbol U_i^n\\f$ as follows:\n     * \\f{align}\n     *   \\alpha_i^n\\;=\\;\\frac{N_i^n}{D_i^n},\n     *   \\quad\n     *   N_i^n\\;:=\\;\\left|a_i^n- \\eta'(\\boldsymbol U^n_i)\\cdot\\boldsymbol\n     *   b_i^n +\\frac{\\eta(\\boldsymbol U^n_i)}{\\rho_i^n}\\big(\\boldsymbol\n     *   b_i^n\\big)_1\\right|,\n     *   \\quad\n     *   D_i^n\\;:=\\;\\left|a_i^n\\right| +\n     *   \\sum_{k=1}^{d+1}\\left|\\big(\\eta'(\\boldsymbol U^n_i)\\big)_k-\n     *   \\delta_{1k}\\frac{\\eta(\\boldsymbol U^n_i)}{\\rho_i^n}\\right|\n     *   \\,\\left|\\big(\\boldsymbol b_i^n\\big)_k\\right|,\n     * \\f}\n     * where where \\f$\\big(\\,.\\,\\big)_k\\f$ denotes the \\f$k\\f$-th component\n     * of a vector, \\f$\\delta_{ij}\\f$ is Kronecker's delta, and where we have\n     * set\n     * \\f{align}\n     *   a_i^n \\;:=\\;\n     *   \\sum_{j\\in\\mathcal{I}_i}\\left(\\frac{\\eta(\\boldsymbol U_j^n)}{\\rho_j^n}\n     *   -\\frac{\\eta(\\boldsymbol U_i^n)}{\\rho_i^n}\\right)\\,\n     *   \\boldsymbol m_j^n\\cdot\\boldsymbol c_{ij},\n     *   \\qquad\n     *   \\boldsymbol b_i^n \\;:=\\;\n     *   \\sum_{j\\in\\mathcal{I}_i}\\left(\\mathbf{f}(\\boldsymbol U_j^n)-\n     *   \\mathbf{f}(\\boldsymbol U_i^n)\\right)\\cdot\\boldsymbol c_{ij},\n     * \\f}\n     *\n     * @ingroup EulerEquations\n     */\n    template <int dim, typename Number = double>\n    class Indicator\n    {\n    public:\n      /**\n       * @name Typedefs and constexpr constants\n       */\n      //@{\n\n      using View = HyperbolicSystemView<dim, Number>;\n\n      using ScalarNumber = typename View::ScalarNumber;\n\n      static constexpr auto problem_dimension = View::problem_dimension;\n\n      using state_type = typename View::state_type;\n\n      using flux_type = typename View::flux_type;\n\n      using precomputed_type = typename View::precomputed_type;\n\n      using PrecomputedVector = typename View::PrecomputedVector;\n\n      using Parameters = IndicatorParameters<ScalarNumber>;\n\n      //@}\n      /**\n       * @name Stencil-based computation of indicators\n       *\n       * Intended usage:\n       * ```\n       * Indicator<dim, Number> indicator;\n       * for (unsigned int i = n_internal; i < n_owned; ++i) {\n       *   // ...\n       *   indicator.reset(i, U_i);\n       *   for (unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {\n       *     // ...\n       *     indicator.accumulate(js, U_j, c_ij);\n       *   }\n       *   indicator.alpha(hd_i);\n       * }\n       * ```\n       */\n      //@{\n\n      /**\n       * Constructor taking a HyperbolicSystem instance as argument\n       */\n      Indicator(const HyperbolicSystem &hyperbolic_system,\n                const Parameters &parameters,\n                const PrecomputedVector &precomputed_values)\n          : hyperbolic_system(hyperbolic_system)\n          , parameters(parameters)\n          , precomputed_values(precomputed_values)\n      {\n      }\n\n      /**\n       * Reset temporary storage and initialize for a new row corresponding\n       * to state vector U_i.\n       */\n      void reset(const unsigned int i, const state_type &U_i);\n\n      /**\n       * When looping over the sparsity row, add the contribution associated\n       * with the neighboring state U_j.\n       */\n      void accumulate(const unsigned int *js,\n                      const state_type &U_j,\n                      const dealii::Tensor<1, dim, Number> &c_ij);\n\n      /**\n       * Return the computed alpha_i value.\n       */\n      Number alpha(const Number h_i) const;\n\n      //@}\n\n    private:\n      /**\n       * @name\n       */\n      //@{\n\n      const HyperbolicSystem &hyperbolic_system;\n      const Parameters &parameters;\n      const PrecomputedVector &precomputed_values;\n\n      Number rho_i_inverse = 0.;\n      Number eta_i = 0.;\n      flux_type f_i;\n      state_type d_eta_i;\n\n      Number left = 0.;\n      state_type right;\n\n      //@}\n    };\n\n\n    /*\n     * -------------------------------------------------------------------------\n     * Inline definitions\n     * -------------------------------------------------------------------------\n     */\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline void\n    Indicator<dim, Number>::reset(const unsigned int i, const state_type &U_i)\n    {\n      /* Entropy viscosity commutator: */\n\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      const auto &[new_s_i, new_eta_i] =\n          precomputed_values.template read_tensor<Number, precomputed_type>(i);\n\n      const auto rho_i = view.density(U_i);\n      rho_i_inverse = Number(1.) / rho_i;\n      eta_i = new_eta_i;\n\n      d_eta_i = view.harten_entropy_derivative(U_i);\n      d_eta_i[0] -= eta_i * rho_i_inverse;\n      f_i = view.f(U_i);\n\n      left = 0.;\n      right = 0.;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline void Indicator<dim, Number>::accumulate(\n        const unsigned int *js,\n        const state_type &U_j,\n        const dealii::Tensor<1, dim, Number> &c_ij)\n    {\n      /* Entropy viscosity commutator: */\n\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      const auto &[s_j, eta_j] =\n          precomputed_values.template read_tensor<Number, precomputed_type>(js);\n\n      const auto rho_j = view.density(U_j);\n      const auto rho_j_inverse = Number(1.) / rho_j;\n\n      const auto m_j = view.momentum(U_j);\n      const auto f_j = view.f(U_j);\n\n      const auto entropy_flux =\n          (eta_j * rho_j_inverse - eta_i * rho_i_inverse) * (m_j * c_ij);\n\n      left += entropy_flux;\n      for (unsigned int k = 0; k < problem_dimension; ++k) {\n        const auto component = (f_j[k] - f_i[k]) * c_ij;\n        right[k] += component;\n      }\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    Indicator<dim, Number>::alpha(const Number hd_i) const\n    {\n      /* Entropy viscosity commutator: */\n\n      Number numerator = left;\n      Number denominator = std::abs(left);\n      for (unsigned int k = 0; k < problem_dimension; ++k) {\n        numerator -= d_eta_i[k] * right[k];\n        denominator += std::abs(d_eta_i[k] * right[k]);\n      }\n\n      const auto quotient =\n          std::abs(numerator) / (denominator + hd_i * std::abs(eta_i));\n\n      return std::min(Number(1.), parameters.evc_factor() * quotient);\n    }\n  } // namespace Euler\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/initial_state_astro_jet.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  namespace EulerInitialStates\n  {\n    /**\n     * The astrophysical jet problem introduced in @cite Zhang-Shu-2010 without\n     * radiative cooling. General set up in standard SI units is:\n     *\n     * Ambient state: (rho, u, p) = (5,  0,  0.4127);\n     * Mach 80 jet:   (rho, u, p) = (5,  30, 0.4127), T = 0.07;\n     * Mach 200 jet:  (rho, u, p) = (5, 800, 0.4127), T = 0.001;\n     *\n     * See section 4.4 of reference for more details.\n     *\n     * @ingroup EulerEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class AstroJet : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n      using state_type_1d = typename Description::\n          template HyperbolicSystemView<1, Number>::state_type;\n\n      AstroJet(const HyperbolicSystem &hyperbolic_system,\n               const std::string subsection)\n          : InitialState<Description, dim, Number>(\"astro jet\", subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n        jet_width_ = 0.05;\n        this->add_parameter(\"jet width\",\n                            jet_width_,\n                            \"The width of the jet coming out of boundary\");\n\n        jet_state_[0] = 5.0;\n        jet_state_[1] = 30.0;\n        if constexpr (View::have_energy_equation)\n          jet_state_[2] = 0.4127;\n        this->add_parameter(\n            \"primitive jet state\",\n            jet_state_,\n            \"1d primitive state [rho, u, p] for jet state (or [rho, u] for \"\n            \"the barotropic Euler module)\");\n\n        ambient_state_[0] = 5.0;\n        ambient_state_[1] = 0.0;\n        if constexpr (View::have_energy_equation)\n          ambient_state_[2] = 0.4127;\n        this->add_parameter(\n            \"primitive ambient right\",\n            ambient_state_,\n            \"1d primitive state [rho, u, p] for ambient state (or [rho, u] for \"\n            \"the barotropic Euler module)\");\n\n        const auto convert_states = [&]() {\n          const auto view = hyperbolic_system_.template view<dim, Number>();\n          state_left_ = view.from_initial_state(jet_state_);\n          state_right_ = view.from_initial_state(ambient_state_);\n        };\n        this->parse_parameters_call_back.connect(convert_states);\n        convert_states();\n      }\n\n      state_type compute(const dealii::Point<dim> &point, Number /*t*/) final\n      {\n        return (point[0] < 1.e-12 && std::abs(point[1]) <= jet_width_\n                    ? state_left_\n                    : state_right_);\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n      Number jet_width_;\n\n      state_type_1d jet_state_;\n      state_type_1d ambient_state_;\n\n      state_type state_left_;\n      state_type state_right_;\n    };\n  } // namespace EulerInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/initial_state_becker_solution.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2022 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  namespace EulerInitialStates\n  {\n    /**\n     * The Becker solution.\n     *\n     * An analytic solution of the compressible Navier-Stokes system\n     * as described in @cite Becker1922.\n     *\n     * The initial state is a 1D stationary, viscous shock that is expanded\n     * to 2D/3D if necessary with an additional Galilei transform to add a\n     * velocity. Internally, the routine solves the equation\n     *  \\f{equation}\n     *    x = \\frac{2}{\\gamma+1} \\frac{\\kappa}{m_0 c_v}\n     *    \\Big\\{\\frac{v_0}{v_0-v_1}\\log\\Big(\\frac{v_0-v(x)}{v_0-v_{01}}\\Big)\n     *    - \\frac{v_1}{v_0-v_1}\\log\\Big(\\frac{v(x)-v_1}{v_{01}-v_1}\\Big)\\Big\\}.\n     *  \\f}\n     *  to high accuracy to recover the function @f$v(x)@f$. This\n     *  information is then used to compute density and internal energy as\n     *  follows:\n     *  \\f{equation}\n     *    \\rho(x) = \\frac{m_0}{v(x)},\n     *    \\qquad\n     *    e(x) = \\frac{1}{2\\gamma}\\Big(\\frac{\\gamma+1}{\\gamma-1}v_{01}^2 -\n     *    v^2(x)\\Big).\n     *  \\f}\n     *  For details see the dicussion in @cite ryujin-2021-2 Section 7.2.\n     *\n     * @note This class returns the analytic solution as a function of time\n     * @p t and position @p x.\n     *\n     * @ingroup EulerEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class BeckerSolution : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n\n      BeckerSolution(const HyperbolicSystem &hyperbolic_system,\n                     const std::string &subsection)\n          : InitialState<Description, dim, Number>(\"becker solution\",\n                                                   subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n        gamma_ = 1.4;\n        if constexpr (!View::have_gamma) {\n          this->add_parameter(\"gamma\", gamma_, \"The ratio of specific heats\");\n        }\n\n        velocity_ = 0.2;\n        this->add_parameter(\"velocity galilean frame\",\n                            velocity_,\n                            \"Velocity used to apply a Galilean transformation \"\n                            \"to the otherwise stationary solution\");\n\n        velocity_left_ = 1.0;\n        this->add_parameter(\n            \"velocity left\", velocity_left_, \"Left limit velocity\");\n\n        velocity_right_ = 7. / 27.;\n        this->add_parameter(\n            \"velocity right\", velocity_right_, \"Right limit velocity\");\n\n        density_left_ = 1.0;\n        this->add_parameter(\n            \"density left\", density_left_, \"Left limit density\");\n\n        mu_ = 0.01;\n        this->add_parameter(\"mu\", mu_, \"Shear viscosity\");\n\n        /* Callback: */\n\n        dealii::ParameterAcceptor::parse_parameters_call_back.connect([this]() {\n          const auto view = hyperbolic_system_.template view<dim, Number>();\n\n          if constexpr (View::have_gamma) {\n            gamma_ = view.gamma();\n          }\n\n          AssertThrow(\n              velocity_left_ > velocity_right_,\n              dealii::ExcMessage(\"The left limiting velocity must be greater \"\n                                 \"than the right limiting velocity\"));\n\n          AssertThrow(velocity_left_ > 0.,\n                      dealii::ExcMessage(\n                          \"The left limiting velocity must be positive\"));\n\n          /*\n           * Set up all helper functions and quantities:\n           */\n\n          const double velocity_origin =\n              std::sqrt(velocity_left_ * velocity_right_);\n\n          /* Prefactor as given in: (7.1) */\n\n          const double Pr = 0.75;\n          const double factor = 2. * gamma_ / (gamma_ + 1.) //\n                                * mu_ / (density_left_ * velocity_left_ * Pr);\n\n          psi = [=, this](double x, double v) {\n            const double c_l =\n                velocity_left_ / (velocity_left_ - velocity_right_);\n            const double c_r =\n                velocity_right_ / (velocity_left_ - velocity_right_);\n            const double log_l = std::log(velocity_left_ - v) -\n                                 std::log(velocity_left_ - velocity_origin);\n            const double log_r = std::log(v - velocity_right_) -\n                                 std::log(velocity_origin - velocity_right_);\n\n            const double value = factor * (c_l * log_l - c_r * log_r) - x;\n\n            const double derivative = factor * (-c_l / (velocity_left_ - v) -\n                                                c_r / (v - velocity_right_));\n\n            return std::make_tuple(value, derivative);\n          };\n\n          /* Determine cut-off points: */\n\n          constexpr double tol = 1.e-12;\n\n          const double x_left = std::get<0>(\n              psi(0., (1. - tol) * velocity_left_ + tol * velocity_right_));\n\n          const double x_right = std::get<0>(\n              psi(0., tol * velocity_left_ + (1. - tol) * velocity_right_));\n\n          const double norm = (x_right - x_left) * tol;\n\n          /* Root finding algorithm: */\n\n          find_velocity = [=, this](double x) {\n            /* Return extremal cases: */\n            if (x <= x_left)\n              return double(velocity_left_);\n            if (x >= x_right)\n              return double(velocity_right_);\n\n            /* Interpolate initial guess: */\n            const auto nu =\n                0.5 * std::tanh(10. * (x - 0.5 * (x_right + x_left)) /\n                                (x_right - x_left));\n            double v =\n                velocity_left_ * (0.5 - nu) + velocity_right_ * (nu + 0.5);\n\n            auto [f, df] = psi(x, v);\n\n            while (std::abs(f) > norm) {\n              const double v_next = v - f / df;\n\n              /* Also break if we made no progress: */\n              if (std::abs(v_next - v) <\n                  tol * 0.5 * (velocity_right_ + velocity_left_)) {\n                v = v_next;\n                break;\n              }\n\n              if (v_next < velocity_right_)\n                v = 0.5 * (velocity_right_ + v);\n              else if (v_next > velocity_left_)\n                v = 0.5 * (velocity_left_ + v);\n              else\n                v = v_next;\n\n              const auto [new_f, new_df] = psi(x, v);\n              f = new_f;\n              df = new_df;\n            }\n\n            return v;\n          }; /* find_velocity */\n        });\n      }\n\n      state_type compute(const dealii::Point<dim> &point, Number t) final\n      {\n        const auto view = hyperbolic_system_.template view<dim, Number>();\n\n        /* (7.2) */\n        const double R_infty = (gamma_ + 1) / (gamma_ - 1);\n\n        /* (7.3) */\n        const double x = point[0] - velocity_ * t;\n        const double v = find_velocity(x);\n        Assert(v >= velocity_right_, dealii::ExcInternalError());\n        Assert(v <= velocity_left_, dealii::ExcInternalError());\n        const double rho = density_left_ * velocity_left_ / v;\n        Assert(rho > 0., dealii::ExcInternalError());\n        const double e = 1. / (2. * gamma_) *\n                         (R_infty * velocity_left_ * velocity_right_ - v * v);\n        Assert(e > 0., dealii::ExcInternalError());\n\n        using state_type_1d = typename Description::\n            template HyperbolicSystemView<1, Number>::state_type;\n\n        state_type_1d result;\n        result[0] = Number(rho);\n        result[1] = Number(rho * (velocity_ + v));\n        if constexpr (View::have_energy_equation)\n          result[2] =\n              Number(rho * (e + 0.5 * (velocity_ + v) * (velocity_ + v)));\n\n        return view.expand_state(result);\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n      Number gamma_;\n\n      Number velocity_;\n      Number velocity_left_;\n      Number velocity_right_;\n      Number density_left_;\n      Number mu_;\n      std::function<std::tuple<double, double>(double, double)> psi;\n      std::function<double(double)> find_velocity;\n    };\n\n  } // namespace EulerInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/initial_state_contrast.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2022 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  namespace EulerInitialStates\n  {\n    /**\n     * An initial state formed by a contrast of a given \"left\" and \"right\"\n     * primitive (initial) state.\n     *\n     * @note The @p t argument is ignored. This class does not evolve a\n     * possible shock front in time. If you need correct time-dependent\n     * Dirichlet data use @ref ShockFront instead.\n     *\n     * @ingroup EulerEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class Contrast : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n      using state_type_1d = typename Description::\n          template HyperbolicSystemView<1, Number>::state_type;\n\n      Contrast(const HyperbolicSystem &hyperbolic_system,\n               const std::string subsection)\n          : InitialState<Description, dim, Number>(\"contrast\", subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n        primitive_left_[0] = 1.4;\n        primitive_left_[1] = 0.;\n        if constexpr (View::have_energy_equation)\n          primitive_left_[2] = 1.;\n        this->add_parameter(\"primitive state left\",\n                            primitive_left_,\n                            \"1d primitive state [rho, u, p] on the left (or \"\n                            \"[rho, u] for the barotropic Euler module)\");\n\n        primitive_right_[0] = 1.4;\n        primitive_right_[1] = 0.;\n        if constexpr (View::have_energy_equation)\n          primitive_right_[2] = 1.;\n        this->add_parameter(\"primitive state right\",\n                            primitive_right_,\n                            \"1d primitive state [rho, u, p] on the right (or \"\n                            \"[rho, u] for the barotropic Euler module)\");\n\n        const auto convert_states = [&]() {\n          const auto view = hyperbolic_system_.template view<dim, Number>();\n          state_left_ = view.from_initial_state(primitive_left_);\n          state_right_ = view.from_initial_state(primitive_right_);\n        };\n        this->parse_parameters_call_back.connect(convert_states);\n        convert_states();\n      }\n\n      state_type compute(const dealii::Point<dim> &point, Number /*t*/) final\n      {\n        return (point[0] > 0. ? state_right_ : state_left_);\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n      state_type_1d primitive_left_;\n      state_type_1d primitive_right_;\n\n      state_type state_left_;\n      state_type state_right_;\n    };\n  } // namespace EulerInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/initial_state_exact_riemann_solution.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2025 by the ryujin authors\n// Copyright (C) 2025 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\n#include <deal.II/base/tensor.h>\n\n#include <cmath>\n\n// #define DEBUG_SOLUTION\n\nnamespace ryujin\n{\n  namespace EulerInitialStates\n  {\n    /**\n     * The exact Riemann solution.\n     *\n     * This initial class computes the analytic solution for the\n     * compressible Euler equations with ideal gas equation of state.\n     *\n     * @note This class returns the analytic solution as a function of time\n     * @p t and position @p x.\n     *\n     * @ingroup EulerEquations\n     */\n\n    template <typename Description, int dim, typename Number>\n    class ExactRiemannSolution : public InitialState<Description, dim, Number>\n    {\n    public:\n      //@{\n\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n\n      using ScalarNumber = typename View::ScalarNumber;\n\n\n      ExactRiemannSolution(const HyperbolicSystem &hyperbolic_system,\n                           const std::string subsection)\n          : InitialState<Description, dim, Number>(\"exact riemann solution\",\n                                                   subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n        gamma_ = 1.4;\n        if constexpr (!View::have_gamma) {\n          this->add_parameter(\"gamma\", gamma_, \"The ratio of specific heats\");\n        }\n\n        primitive_left_[0] = 1.4;\n        primitive_left_[1] = 0.0;\n        primitive_left_[2] = 1.0;\n        this->add_parameter(\"primitive state left\",\n                            primitive_left_,\n                            \"1d primitive state [rho, u, p] (for the \"\n                            \"polytropic gas EOS) on the left\");\n\n        primitive_right_[0] = 1.4;\n        primitive_right_[1] = 0.0;\n        primitive_right_[2] = 1.0;\n        this->add_parameter(\"primitive state right\",\n                            primitive_right_,\n                            \"1d primitive state [rho, u, p] (for the \"\n                            \"polytropic gas EOS) on the right\");\n\n        // Convert the primitive states to conserved states\n        const auto prepare_riemann_data = [&]() {\n          const auto view = hyperbolic_system_.template view<dim, Number>();\n          if constexpr (View::have_gamma) {\n            gamma_ = view.gamma();\n          }\n\n          const Number p_L = primitive_left_[2];\n          const Number p_R = primitive_right_[2];\n\n          p_star_ = compute_pstar(p_L, p_R, primitive_left_, primitive_right_);\n\n          const Number u_L = primitive_left_[1];\n          u_star_ = u_L - fZofP(p_star_, primitive_left_);\n\n#ifdef DEBUG_SOLUTION\n          const Number u_R = primitive_right_[1];\n          std::cout << \"left data          = \" << primitive_left_\n                    << \"\\nright data       = \" << primitive_right_\n                    << \"\\np_star           = \" << p_star_\n                    << \"\\nu_star           = \" << u_star_\n                    << \"\\nVerifying u_star = \"\n                    << u_R + fZofP(p_star_, primitive_right_) << std::endl;\n#endif\n\n          lambda_left_minus_ = lambda(p_star_, primitive_left_, -1.);\n          lambda_left_plus_ =\n              lambda_intermediate(p_star_, primitive_left_, -1.);\n          lambda_right_minus_ =\n              lambda_intermediate(p_star_, primitive_right_, 1.);\n          lambda_right_plus_ = lambda(p_star_, primitive_right_, 1.);\n\n\n#ifdef DEBUG_SOLUTION\n          std::cout << \"lambda_left_minus  =  \" << lambda_left_minus_\n                    << \"\\nlambda_left_plus   =  \" << lambda_left_plus_\n                    << \"\\nlambda_right_minus = \" << lambda_right_minus_\n                    << \"\\nlambda_right_plus  =  \" << lambda_right_plus_\n                    << std::endl;\n#endif\n        };\n\n        this->parse_parameters_call_back.connect(prepare_riemann_data);\n        prepare_riemann_data();\n      }\n\n\n      state_type compute(const dealii::Point<dim> &point, Number t) final\n      {\n        const auto view = hyperbolic_system_.template view<dim, Number>();\n\n        const double &x = point[0];\n\n        const Number xi = x / t;\n\n        dealii::Tensor<1, 3, Number> primitive_state;\n\n        if (t < 1.e-14 && x < 0.) {\n          primitive_state = primitive_left_;\n#ifdef DEBUG_SOLUTION\n          std::cout << \"Left primitive state: \" << primitive_state << std::endl;\n#endif\n\n        } else if (t < 1.e-14 && x > 0.) {\n          primitive_state = primitive_right_;\n#ifdef DEBUG_SOLUTION\n          std::cout << \"Right primitive state: \" << primitive_state\n                    << std::endl;\n#endif\n\n        } else if (xi < lambda_left_minus_) {\n          /* Left state: */\n          primitive_state = primitive_left_;\n#ifdef DEBUG_SOLUTION\n          std::cout << \"Left primitive state: \" << primitive_state << std::endl;\n#endif\n\n        } else if (xi < lambda_left_plus_) {\n          const auto c_LL =\n              expansion_solution(p_star_, xi, primitive_left_, -1.);\n          primitive_state = c_LL;\n#ifdef DEBUG_SOLUTION\n          std::cout << \"Left expansion state: \" << primitive_state << std::endl;\n#endif\n\n        } else if (xi < u_star_) {\n          primitive_state = cstar_solution(p_star_, u_star_, primitive_left_);\n\n          const Number p_L = primitive_left_[2];\n          if (p_star_ < p_L)\n            primitive_state = expansion_solution(\n                p_star_, lambda_left_plus_, primitive_left_, -1.);\n#ifdef DEBUG_SOLUTION\n          std::cout << \"Left cstar state: \" << primitive_state << std::endl;\n#endif\n\n        } else if (xi < lambda_right_minus_) {\n          primitive_state = cstar_solution(p_star_, u_star_, primitive_right_);\n\n          const Number p_R = primitive_right_[2];\n          if (p_star_ < p_R)\n            primitive_state = expansion_solution(\n                p_star_, lambda_right_minus_, primitive_right_, 1.);\n#ifdef DEBUG_SOLUTION\n          std::cout << \"Right cstar state: \" << primitive_state << std::endl;\n#endif\n\n        } else if (xi < lambda_right_plus_) {\n          primitive_state =\n              expansion_solution(p_star_, xi, primitive_right_, 1.);\n#ifdef DEBUG_SOLUTION\n          std::cout << \"Right expansion state: \" << primitive_state\n                    << std::endl;\n#endif\n\n        } else {\n          /* Right state: */\n          primitive_state = primitive_right_;\n#ifdef DEBUG_SOLUTION\n          std::cout << \"Right primitive state: \" << primitive_state\n                    << std::endl;\n#endif\n        }\n\n        using state_type_1d = typename Description::\n            template HyperbolicSystemView<1, Number>::state_type;\n        static_assert(state_type_1d::dimension <=\n                      dealii::Tensor<1, 3, Number>::dimension);\n\n        state_type_1d result;\n        for (unsigned int i = 0; i < state_type_1d::dimension; ++i)\n          result[i] = primitive_state[i];\n        return view.from_initial_state(result);\n      }\n\n    private:\n      //@}\n      /**\n       * Runtime parameters\n       */\n      //@{\n\n      Number gamma_;\n\n      dealii::Tensor<1, 3, Number> primitive_left_;\n      dealii::Tensor<1, 3, Number> primitive_right_;\n\n      //@}\n      /**\n       * Private fields\n       */\n      //@{\n\n      const HyperbolicSystem &hyperbolic_system_;\n\n      Number p_star_;\n      Number u_star_;\n      Number lambda_left_minus_;\n      Number lambda_left_plus_;\n      Number lambda_right_minus_;\n      Number lambda_right_plus_;\n\n      //@}\n      /**\n       * Internal helper functions for solving the exact Riemann problem\n       */\n      //@{\n\n      Number fZofP(const Number &p_in,\n                   const dealii::Tensor<1, 3, Number> &data_in) const\n      {\n        // Get left/right data\n        const Number rho_Z = data_in[0];\n        const Number p_Z = data_in[2];\n\n        const Number c_Z = std::sqrt(gamma_ * p_Z / rho_Z);\n\n        const Number A_Z = 2. / (gamma_ + 1.) / rho_Z;\n        const Number B_Z = (gamma_ - 1.) / (gamma_ + 1.) * p_Z;\n\n        const Number exp = 0.5 * (gamma_ - 1.) / gamma_;\n        Number left_brach = 2. * c_Z / (gamma_ - 1.);\n        left_brach *= (std::pow(p_in / p_Z, exp) - 1.);\n\n        Number f_of_p = (p_in - p_Z) * std::sqrt(A_Z / (p_in + B_Z));\n\n        if (p_in <= p_Z)\n          f_of_p = left_brach;\n\n        return f_of_p;\n      }\n\n\n      Number dfZofP(const Number &p_in,\n                    const dealii::Tensor<1, 3, Number> &data_in) const\n      {\n        // Get left/right data\n        const Number rho_Z = data_in[0];\n        const Number p_Z = data_in[2];\n\n        const Number c_Z = std::sqrt(gamma_ * p_Z / rho_Z);\n\n        const Number A_Z = 2. / (gamma_ + 1.) / rho_Z;\n        const Number B_Z = (gamma_ - 1.) / (gamma_ + 1.) * p_Z;\n\n        Number exp = 0.5 * (gamma_ - 1.) / gamma_;\n        Number left_brach = 2. * c_Z / (gamma_ - 1.) * exp;\n        exp -= 1.;\n\n        left_brach *= std::pow(p_in / p_Z, exp - 1.) / p_Z;\n\n        Number right_branch = std::pow(A_Z / (p_in + B_Z), 1.5);\n        right_branch *= (2. * B_Z + p_in + p_Z) / (2. * A_Z);\n\n        Number df_of_p = right_branch;\n\n        if (p_in <= p_Z)\n          df_of_p = left_brach;\n\n        return df_of_p;\n      }\n\n\n      Number dphi(const Number &p_in,\n                  const dealii::Tensor<1, 3, Number> &data_left,\n                  const dealii::Tensor<1, 3, Number> &data_right) const\n      {\n        return dfZofP(p_in, data_left) + dfZofP(p_in, data_right);\n      }\n\n\n      Number phi(const Number &p_in,\n                 const dealii::Tensor<1, 3, Number> &data_left,\n                 const dealii::Tensor<1, 3, Number> &data_right) const\n      {\n        const Number u_L = data_left[1];\n        const Number u_R = data_right[1];\n\n        return fZofP(p_in, data_right) + fZofP(p_in, data_left) + u_R - u_L;\n      }\n\n\n      Number lambda(const Number &p_in,\n                    const dealii::Tensor<1, 3, Number> &data_in,\n                    const Number &sign) const\n      {\n        // Get left/right data\n        const Number rho_Z = data_in[0];\n        const Number u_Z = data_in[1];\n        const Number p_Z = data_in[2];\n\n        const Number c_Z = std::sqrt(gamma_ * p_Z / rho_Z);\n\n        const Number radicand =\n            1. + 0.5 * (gamma_ + 1.) / gamma_ * std::max(p_in / p_Z - 1., 0.);\n\n        return u_Z + sign * c_Z * std::sqrt(radicand);\n      }\n\n\n      Number lambda_intermediate(const Number &p_in,\n                                 const dealii::Tensor<1, 3, Number> &data_in,\n                                 const Number &sign) const\n      {\n        const Number rho_Z = data_in[0];\n        const Number u_Z = data_in[1];\n        const Number p_Z = data_in[2];\n\n        const Number c_Z = std::sqrt(gamma_ * p_Z / rho_Z);\n\n        const auto lambda_value = lambda(p_in, data_in, sign);\n\n        const Number f_of_p = fZofP(p_in, data_in);\n\n        const Number exp = 0.5 * (gamma_ - 1.) / gamma_;\n        const Number expansion_speed =\n            u_Z + sign * (f_of_p + c_Z * std::pow(p_in / p_Z, exp));\n\n        Number result = lambda_value;\n        if (p_in < p_Z)\n          result = expansion_speed;\n\n        return result;\n      }\n\n\n      dealii::Tensor<1, 3, Number>\n      cstar_solution(const Number &p_star,\n                     const Number &u_star,\n                     const dealii::Tensor<1, 3, Number> &data_in) const\n      {\n        const Number rho_Z = data_in[0];\n        const Number p_Z = data_in[2];\n\n        // Define rho_star\n        const Number p_ratio = p_star / p_Z;\n        const Number gamma_ratio = (gamma_ - 1.) / (gamma_ + 1.);\n\n        const Number numerator = rho_Z * (p_ratio + gamma_ratio);\n        const Number denominator = gamma_ratio * p_ratio + 1.;\n\n        Number rho_star = numerator / denominator;\n\n        auto result = data_in;\n        result[0] = rho_star;\n        result[1] = u_star;\n        result[2] = p_star;\n\n        return result;\n      }\n\n\n      dealii::Tensor<1, 3, Number>\n      expansion_solution(const Number & /*p_star*/,\n                         const Number &xi,\n                         const dealii::Tensor<1, 3, Number> &data_in,\n                         const Number &sign) const\n      {\n        const Number rho_Z = data_in[0];\n        const Number u_Z = data_in[1];\n        const Number p_Z = data_in[2];\n\n        const Number c_Z = std::sqrt(gamma_ * p_Z / rho_Z);\n\n        // Define rho_expansion\n        const Number gamma_ratio = (gamma_ - 1.) / (gamma_ + 1.);\n\n        const Number first = 2. / (gamma_ + 1.);\n        const Number second = gamma_ratio / c_Z * (u_Z - xi);\n        const Number exp = 2. / (gamma_ - 1.);\n\n        Number rho_expansion = rho_Z * std::pow(first - sign * second, exp);\n\n        // Define p_expansion\n        const Number factor = p_Z / std::pow(rho_Z, gamma_);\n        const Number p_expansion = factor * std::pow(rho_expansion, gamma_);\n\n        // Define u_expansion\n        const Number u_expansion = u_Z + sign * fZofP(p_expansion, data_in);\n\n        auto result = data_in;\n        result[0] = rho_expansion;\n        result[1] = u_expansion;\n        result[2] = p_expansion;\n\n        return result;\n      }\n\n\n      /**\n       * Compute pstar using the quadratic_newton_step()\n       */\n      double compute_pstar(double p_1,\n                           double p_2,\n                           dealii::Tensor<1, 3, Number> data_1,\n                           dealii::Tensor<1, 3, Number> data_2)\n      {\n        constexpr Number eps = std::numeric_limits<Number>::epsilon();\n\n        // Ensure that p_1 <= p_2\n\n        if (p_1 > p_2) {\n          std::swap(p_1, p_2);\n          std::swap(data_1, data_2);\n        }\n\n#ifdef DEBUG\n        {\n          const double phi_1 = phi(p_1, data_1, data_2);\n          const double phi_2 = phi(p_2, data_1, data_2);\n          Assert(phi_1 * phi_2 <= 0.,\n                 dealii::ExcMessage(\n                     \"Euler::ExactRiemannSolver: failed to compute p_star.\"));\n        }\n#endif\n\n        //\n        // We simply compute the root of phi with a bisection method down\n        // to machine precision. This is not terribly efficient but luckily\n        // happens only once during initialization.\n        //\n\n#ifdef DEBUG_SOLUTION\n        std::cout << \"Computing p_star with a bisection method.\" << std::endl;\n#endif\n\n        unsigned int iter = 0;\n        for (; iter < 200; ++iter) {\n\n          // Check for convergence:\n          if (std::abs(p_2 - p_1) < 10. * eps * std::max(p_1, p_2)) {\n            break;\n          }\n\n          const double phi_2 = phi(p_2, data_1, data_2);\n\n#ifdef DEBUG_SOLUTION\n          const double phi_1 = phi(p_1, data_1, data_2);\n\n          std::cout << \"\\niter: \" << iter << \"\\n\";\n          std::cout << \"p_1: \" << p_1 << \"\\n\";\n          std::cout << \"p_2: \" << p_2 << \"\\n\";\n          std::cout << \"phi_1: \" << phi_1 << \"\\n\";\n          std::cout << \"phi_2: \" << phi_2 << \"\\n\";\n#endif\n\n          const auto p_m = 0.5 * (p_2 + p_1);\n          const double phi_m = phi(p_m, data_1, data_2);\n\n          if (phi_m * phi_2 >= 0.) {\n            p_2 = p_m;\n          } else {\n            p_1 = p_m;\n          }\n        }\n\n#ifdef DEBUG_SOLUTION\n        const double phi_2 = phi(p_2, data_1, data_2);\n        std::cout << \"After \" << iter << \" iterations:\"\n                  << \"\\np_star =      \" << p_2 << \"\\nphi(p_star) = \" << phi_2\n                  << \"\\n|p_2 - p_1| = \" << std::abs(p_2 - p_1) << std::endl;\n#endif\n\n        return p_2;\n      }\n\n      //@}\n    };\n  } // namespace EulerInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/initial_state_four_state_contrast.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  namespace EulerInitialStates\n  {\n    /**\n     * A 2D extension of the \"contrast\" initial state consisting of 4 different\n     * states separated at x = 0 and y = 0. Visually, this looks like:\n     *\n     *        state 1  | state 2\n     *        ---------|-----------\n     *        state 3  | state 4\n     *\n     * @note The @p t argument is ignored. This class always returns the\n     * initial configuration.\n     *\n     * @ingroup EulerEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class FourStateContrast : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n      using state_type_2d = typename Description::\n          template HyperbolicSystemView<2, Number>::state_type;\n\n      FourStateContrast(const HyperbolicSystem &hyperbolic_system,\n                        const std::string &subsection)\n          : InitialState<Description, dim, Number>(\"four state contrast\",\n                                                   subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n        primitive_bottom_left_[0] = 1.4;\n        primitive_bottom_left_[1] = 0.;\n        primitive_bottom_left_[2] = 0.;\n        if constexpr (View::have_energy_equation)\n          primitive_bottom_left_[3] = 1.;\n        this->add_parameter(\"primitive state bottom left\",\n                            primitive_bottom_left_,\n                            \"Primitive state [rho, u, v, p] on bottom left (or \"\n                            \"[rho, u] for the barotropic Euler module)\");\n\n        primitive_bottom_right_[0] = 1.4;\n        primitive_bottom_right_[1] = 0.;\n        primitive_bottom_right_[2] = 0.;\n        if constexpr (View::have_energy_equation)\n          primitive_bottom_right_[3] = 1.;\n        this->add_parameter(\"primitive state bottom right\",\n                            primitive_bottom_right_,\n                            \"Primitive state [rho, u, v, p] on bottom right \"\n                            \"(or [rho, u] for the barotropic Euler module)\");\n\n        primitive_top_left_[0] = 1.4;\n        primitive_top_left_[1] = 0.;\n        primitive_top_left_[2] = 0.;\n        if constexpr (View::have_energy_equation)\n          primitive_top_left_[3] = 1.;\n        this->add_parameter(\"primitive state top left\",\n                            primitive_top_left_,\n                            \"Primitive state [rho, u, v, p] on top left (or \"\n                            \"[rho, u] for the barotropic Euler module)\");\n\n        primitive_top_right_[0] = 1.4;\n        primitive_top_right_[1] = 0.;\n        primitive_top_right_[2] = 0.;\n        if constexpr (View::have_energy_equation)\n          primitive_top_right_[3] = 1.;\n        this->add_parameter(\"primitive state top right\",\n                            primitive_top_right_,\n                            \"Primitive state [rho, u, v, p] on top right (or \"\n                            \"[rho, u] for the barotropic Euler module)\");\n\n        const auto convert_states = [&]() {\n          const auto view = hyperbolic_system_.template view<dim, Number>();\n          if constexpr (dim != 1) {\n            state_bottom_left_ =\n                view.from_initial_state(primitive_bottom_left_);\n            state_bottom_right_ =\n                view.from_initial_state(primitive_bottom_right_);\n            state_top_left_ = view.from_initial_state(primitive_top_left_);\n            state_top_right_ = view.from_initial_state(primitive_top_right_);\n          }\n        };\n        this->parse_parameters_call_back.connect(convert_states);\n        convert_states();\n      }\n\n      state_type compute(const dealii::Point<dim> &point, Number /*t*/) final\n      {\n        if constexpr (dim == 1) {\n          AssertThrow(false, dealii::ExcNotImplemented());\n          __builtin_trap();\n\n        } else {\n\n          const auto top = point[0] >= 0. ? state_top_right_ : state_top_left_;\n          const auto bottom =\n              point[0] >= 0. ? state_bottom_right_ : state_bottom_left_;\n          return (point[1] >= 0. ? top : bottom);\n        }\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n      state_type_2d primitive_bottom_left_;\n      state_type_2d primitive_bottom_right_;\n      state_type_2d primitive_top_left_;\n      state_type_2d primitive_top_right_;\n\n      state_type state_bottom_left_;\n      state_type state_bottom_right_;\n      state_type state_top_left_;\n      state_type state_top_right_;\n    };\n  } // namespace EulerInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/initial_state_function.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\n#include <deal.II/base/function_parser.h>\n\nnamespace ryujin\n{\n  namespace EulerInitialStates\n  {\n    /**\n     * Returns an initial state defined by a set of user specified function.\n     *\n     * @ingroup EulerEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class Function : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n\n      Function(const HyperbolicSystem &hyperbolic_system,\n               const std::string subsection)\n          : InitialState<Description, dim, Number>(\"function\", subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n        density_expression_ = \"1.4\";\n        this->add_parameter(\"density expression\",\n                            density_expression_,\n                            \"A function expression describing the density\");\n\n        velocity_x_expression_ = \"3.0\";\n        this->add_parameter(\n            \"velocity x expression\",\n            velocity_x_expression_,\n            \"A function expression describing the x-component of the velocity\");\n\n        if constexpr (dim > 1) {\n          velocity_y_expression_ = \"0.0\";\n          this->add_parameter(\"velocity y expression\",\n                              velocity_y_expression_,\n                              \"A function expression describing the \"\n                              \"y-component of the velocity\");\n        }\n\n        if constexpr (dim > 2) {\n          velocity_z_expression_ = \"0.0\";\n          this->add_parameter(\"velocity z expression\",\n                              velocity_z_expression_,\n                              \"A function expression describing the \"\n                              \"z-component of the velocity\");\n        }\n\n        if constexpr (View::have_energy_equation) {\n          pressure_expression_ = \"1.0\";\n          this->add_parameter(\"pressure expression\",\n                              pressure_expression_,\n                              \"A function expression describing the pressure\");\n        }\n\n        /* Set up all muparser objects */\n\n        const auto set_up_muparser = [this] {\n          using FP = dealii::FunctionParser<dim>;\n          /*\n           * This variant of the constructor initializes the function\n           * parser with support for a time-dependent description involving\n           * a variable »t«:\n           */\n          density_function_ = std::make_unique<FP>(density_expression_);\n          velocity_x_function_ = std::make_unique<FP>(velocity_x_expression_);\n          if constexpr (dim > 1)\n            velocity_y_function_ = std::make_unique<FP>(velocity_y_expression_);\n          if constexpr (dim > 2)\n            velocity_z_function_ = std::make_unique<FP>(velocity_z_expression_);\n          if constexpr (View::have_energy_equation)\n            pressure_function_ = std::make_unique<FP>(pressure_expression_);\n        };\n\n        set_up_muparser();\n        this->parse_parameters_call_back.connect(set_up_muparser);\n      }\n\n      state_type compute(const dealii::Point<dim> &point, Number t) final\n      {\n        const auto view = hyperbolic_system_.template view<dim, Number>();\n        state_type full_primitive_state;\n\n        density_function_->set_time(t);\n        full_primitive_state[0] = density_function_->value(point);\n\n        velocity_x_function_->set_time(t);\n        full_primitive_state[1] = velocity_x_function_->value(point);\n\n        if constexpr (dim > 1) {\n          velocity_y_function_->set_time(t);\n          full_primitive_state[2] = velocity_y_function_->value(point);\n        }\n        if constexpr (dim > 2) {\n          velocity_z_function_->set_time(t);\n          full_primitive_state[3] = velocity_z_function_->value(point);\n        }\n\n        if constexpr (View::have_energy_equation) {\n          pressure_function_->set_time(t);\n          full_primitive_state[1 + dim] = pressure_function_->value(point);\n        }\n\n        return view.from_initial_state(full_primitive_state);\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n      std::string density_expression_;\n      std::string velocity_x_expression_;\n      std::string velocity_y_expression_;\n      std::string velocity_z_expression_;\n      std::string pressure_expression_;\n\n      std::unique_ptr<dealii::FunctionParser<dim>> density_function_;\n      std::unique_ptr<dealii::FunctionParser<dim>> velocity_x_function_;\n      std::unique_ptr<dealii::FunctionParser<dim>> velocity_y_function_;\n      std::unique_ptr<dealii::FunctionParser<dim>> velocity_z_function_;\n      std::unique_ptr<dealii::FunctionParser<dim>> pressure_function_;\n    };\n  } // namespace EulerInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/initial_state_icf_like.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// [LANL Copyright Statement]\n// Copyright (C) 2024 - 2025 by the ryujin authors\n// Copyright (C) 2024 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  namespace EulerInitialStates\n  {\n    /**\n     * An initial state that simulates an \"intertial confinement fusion\" like\n     * problem. The set up consists of three regions: (i) a low density state\n     * inside a perturbed interface; (ii) a high density state outside the\n     * interface; (iii) an incoming shock wave characterized by its Mach number\n     * and the state outside the interface as well as starting location (given\n     * by a radius). The perturbed interface is characterized by the number of\n     * modes and an amplitude.\n     *\n     * @ingroup EulerEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class ICFLike : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n      using state_type_1d = typename Description::\n          template HyperbolicSystemView<1, Number>::state_type;\n\n      ICFLike(const HyperbolicSystem &hyperbolic_system,\n              const std::string subsection)\n          : InitialState<Description, dim, Number>(\"icf like\", subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n        gamma_ = 1.4;\n        if constexpr (!View::have_gamma) {\n          this->add_parameter(\"gamma\", gamma_, \"The ratio of specific heats\");\n        }\n\n        primitive_inside_[0] = 0.1;\n        primitive_inside_[1] = 0.0;\n        primitive_inside_[2] = 1.0;\n        this->add_parameter(\"primitive state inside\",\n                            primitive_inside_,\n                            \"1d primitive state [rho, u, p] (for the \"\n                            \"Noble-Abel gas EOS) inside perturbed interface\");\n\n        primitive_outside_[0] = 1.0;\n        primitive_outside_[1] = 0.0;\n        primitive_outside_[2] = 1.0;\n        this->add_parameter(\"primitive state outside\",\n                            primitive_outside_,\n                            \"1d primitive state [rho, u, p] (for the \"\n                            \"Noble-Abel gas EOS) outside perturbed interface\");\n\n        interface_radius_ = 1.0;\n        this->add_parameter(\n            \"interface radius\", interface_radius_, \"Radius of interface\");\n\n        num_modes_ = 8.0;\n        this->add_parameter(\"number of modes\",\n                            num_modes_,\n                            \"Number of modes for pertburation of interface\");\n\n        amplitude_ = 0.02;\n        this->add_parameter(\n            \"amplitude\", amplitude_, \"Amplitude for interface pertburation\");\n\n        mach_number_ = 3.0;\n        this->add_parameter(\n            \"mach number\", mach_number_, \"Mach number of incoming shock front\");\n\n        shock_radius_ = 1.2;\n        this->add_parameter(\"shock radius\",\n                            shock_radius_,\n                            \"Radial location of incoming shock front\");\n\n        const auto convert_states = [&]() {\n          const auto view = hyperbolic_system_.template view<dim, Number>();\n\n          using state_type_1d = typename Description::\n              template HyperbolicSystemView<1, Number>::state_type;\n          static_assert(state_type_1d::dimension <=\n                        dealii::Tensor<1, 3, Number>::dimension);\n\n          state_type_1d result_inside;\n          state_type_1d result_outside;\n          for (unsigned int i = 0; i < state_type_1d::dimension; ++i) {\n            result_inside[i] = primitive_inside_[i];\n            result_outside[i] = primitive_outside_[i];\n          }\n          state_inside_ = view.from_initial_state(result_inside);\n          state_outside_ = view.from_initial_state(result_outside);\n        };\n        this->parse_parameters_call_back.connect(convert_states);\n        convert_states();\n      };\n\n      state_type compute(const dealii::Point<dim> &point, Number) final\n      {\n        const auto view = hyperbolic_system_.template view<dim, Number>();\n\n        /* Compute polar (and potentially azimuthal) angle: */\n        const auto x = point[0];\n        const auto y = dim > 1 ? point[1] : 0.;\n        const double theta = std::atan2(y, x);\n        double phi = 0.;\n        if constexpr (dim == 3)\n          phi = std::atan2(point[2], std::sqrt(x * x + y * y));\n\n        /* Compute perturbation for interface */\n        const auto omega = num_modes_;\n        const double perturbation =\n            amplitude_ * std::cos(omega * theta) * std::cos(omega * phi);\n\n        if (point.norm() > shock_radius_) {\n          /*\n           * Inside the incoming shock front:\n           */\n\n          const auto r_hat = point / point.norm();\n\n          auto b = Number(0.);\n          if constexpr (View::have_eos_interpolation_b)\n            b = view.eos_interpolation_b();\n\n          const auto &rho_R = primitive_outside_[0];\n          const auto &u_R = primitive_outside_[1];\n          const auto &p_R = primitive_outside_[2];\n          /* a_R^2 = gamma * p / rho / (1 - b * rho) */\n          const Number a_R = std::sqrt(gamma_ * p_R / rho_R / (1 - b * rho_R));\n          const Number mach_R = u_R / a_R;\n\n          auto S3_ = mach_number_ * a_R;\n          const Number delta_mach = mach_R - mach_number_;\n\n          const Number rho_L =\n              rho_R * (gamma_ + Number(1.)) * delta_mach * delta_mach /\n              ((gamma_ - Number(1.)) * delta_mach * delta_mach + Number(2.));\n          const Number u_L =\n              (Number(1.) - rho_R / rho_L) * S3_ + rho_R / rho_L * u_R;\n          const Number p_L = p_R *\n                             (Number(2.) * gamma_ * delta_mach * delta_mach -\n                              (gamma_ - Number(1.))) /\n                             (gamma_ + Number(1.));\n\n          state_type primitive_shock_state;\n          primitive_shock_state[0] = rho_L;\n\n          for (unsigned int i = 0; i < dim; ++i) {\n            primitive_shock_state[i + 1] = 0.;\n          }\n\n          if (point.norm() > 0.) {\n            for (unsigned int i = 0; i < dim; ++i) {\n              primitive_shock_state[i + 1] = -u_L * r_hat[i];\n            }\n          }\n          if constexpr (View::have_energy_equation)\n            primitive_shock_state[1 + dim] = p_L;\n\n          return view.from_initial_state(primitive_shock_state);\n\n        } else if (point.norm() > interface_radius_ + perturbation) {\n          /*\n           * Outside annulus between inner disc and outer shock annulus:\n           */\n\n          return state_outside_;\n\n        } else {\n          /*\n           * Inner disc:\n           */\n\n          return state_inside_;\n        }\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n      Number gamma_;\n\n      dealii::Tensor<1, 3, Number> primitive_inside_;\n      dealii::Tensor<1, 3, Number> primitive_outside_;\n      state_type state_inside_;\n      state_type state_outside_;\n\n      double interface_radius_;\n      double num_modes_;\n      double amplitude_;\n      double shock_radius_;\n      double mach_number_;\n    };\n\n\n  } // namespace EulerInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/initial_state_isentropic_vortex.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2022 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n#include <simd.h>\n\nnamespace ryujin\n{\n  namespace EulerInitialStates\n  {\n    /**\n     * The isentropic vortex.\n     *\n     * An analytic solution for the compressible Euler equations.\n     *\n     * @note This class returns the analytic solution as a function of time\n     * @p t and position @p x.\n     *\n     * @ingroup EulerEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class IsentropicVortex : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n\n      IsentropicVortex(const HyperbolicSystem &hyperbolic_system,\n                       const std::string subsection)\n          : InitialState<Description, dim, Number>(\"isentropic vortex\",\n                                                   subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n        gamma_ = 1.4;\n        if constexpr (!View::have_gamma) {\n          this->add_parameter(\"gamma\", gamma_, \"The ratio of specific heats\");\n        }\n\n        mach_number_ = 2.0;\n        this->add_parameter(\n            \"mach number\", mach_number_, \"Mach number of isentropic vortex\");\n\n        beta_ = 5.0;\n        this->add_parameter(\"beta\", beta_, \"vortex strength beta\");\n      }\n\n      state_type compute(const dealii::Point<dim> &point, Number t) final\n      {\n        const auto view = hyperbolic_system_.template view<dim, Number>();\n\n        if constexpr (View::have_gamma) {\n          gamma_ = view.gamma();\n        }\n\n        /* In 3D we simply project onto the 2d plane: */\n        dealii::Point<2> point_bar;\n        point_bar[0] = point[0] - mach_number_ * t;\n        point_bar[1] = point[1];\n\n        const Number r_square = Number(point_bar.norm_square());\n\n        const Number factor = beta_ / Number(2. * M_PI) *\n                              exp(Number(0.5) - Number(0.5) * r_square);\n\n        const Number T = Number(1.) - (gamma_ - Number(1.)) /\n                                          (Number(2.) * gamma_) * factor *\n                                          factor;\n\n        const Number u = mach_number_ - factor * Number(point_bar[1]);\n        const Number v = factor * Number(point_bar[0]);\n\n        const Number rho = ryujin::pow(T, Number(1.) / (Number(gamma_ - 1.)));\n        const Number p = ryujin::pow(rho, Number(gamma_));\n        const Number E =\n            p / (gamma_ - Number(1.)) + Number(0.5) * rho * (u * u + v * v);\n\n        AssertThrow(dim > 1, dealii::ExcNotImplemented());\n\n        state_type result;\n        result[0] = rho;\n        result[1] = rho * u;\n        if constexpr (dim >= 2)\n          result[2] = rho * v;\n        if constexpr (View::have_energy_equation)\n          result[dim + 1] = E;\n\n        return result;\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n      Number gamma_;\n      Number mach_number_;\n      Number beta_;\n    };\n  } // namespace EulerInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/initial_state_leblanc.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\n#include <cmath>\n\nnamespace ryujin\n{\n  namespace EulerInitialStates\n  {\n    /**\n     * The Le Blanc shocktube.\n     *\n     * An Analytic solution for the compressible Euler equations with\n     * polytropic gas equation of state and \\f$\\gamma = 5./3\\f$.\n     *\n     * @note This class returns the analytic solution as a function of time\n     * @p t and position @p x.\n     *\n     * @ingroup EulerEquations\n     */\n\n    template <typename Description, int dim, typename Number>\n    class LeBlanc : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n\n      using ScalarNumber = typename View::ScalarNumber;\n\n      LeBlanc(const HyperbolicSystem &hyperbolic_system,\n              const std::string subsection)\n          : InitialState<Description, dim, Number>(\"leblanc\", subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n      } /* Constructor */\n\n      state_type compute(const dealii::Point<dim> &point, Number t) final\n      {\n        /*\n         * The Le Blanc shock tube:\n         */\n\n        /* Initial left and right states (rho, u, p): */\n        using state_type_1d = std::array<Number, 3>;\n        constexpr state_type_1d primitive_left{1., 0., Number(2. / 3. * 1.e-1)};\n        constexpr state_type_1d primitive_right{\n            1.e-3, 0., Number(2. / 3. * 1.e-10)};\n\n        /* The intermediate wave-speeds appearing on the Riemann fan: */\n        constexpr Number rarefaction_speed = 0.49578489518897934;\n        constexpr Number contact_velocity = 0.62183867139173454;\n        constexpr Number right_shock_speed = 0.82911836253346982;\n\n        /*\n         * Velocity and pressure are constant across the middle discontinuity,\n         * only the density jumps: it's a contact wave!\n         */\n        constexpr Number pre_contact_density = 5.4079335349316249e-02;\n        constexpr Number post_contact_density = 3.9999980604299963e-03;\n        constexpr Number contact_pressure = 0.51557792765096996e-03;\n\n        state_type_1d primitive_state;\n        const double &x = point[0];\n\n        if (x <= -1.0 / 3.0 * t) {\n          /* Left state: */\n          primitive_state = primitive_left;\n\n        } else if (x < rarefaction_speed * t) {\n          /* Expansion data (with self-similar variable chi): */\n          const double chi = x / t;\n          primitive_state[0] = std::pow(0.75 - 0.75 * chi, 3.0);\n          primitive_state[1] = 0.75 * (1.0 / 3.0 + chi);\n          primitive_state[2] = (1.0 / 15.0) * std::pow(0.75 - 0.75 * chi, 5.0);\n\n        } else if (x < contact_velocity * t) {\n          primitive_state[0] = pre_contact_density;\n          primitive_state[1] = contact_velocity;\n          primitive_state[2] = contact_pressure;\n\n        } else if (x < right_shock_speed * t) {\n          /* Contact-wave data (velocity and pressure are continuous): */\n          primitive_state[0] = post_contact_density;\n          primitive_state[1] = contact_velocity;\n          primitive_state[2] = contact_pressure;\n\n        } else {\n          /* Right state: */\n          primitive_state = primitive_right;\n        }\n\n        state_type conserved_state;\n        {\n          const auto &[rho, u, p] = primitive_state;\n          conserved_state[0] = rho;\n          conserved_state[1] = rho * u;\n          if constexpr (View::have_energy_equation)\n            conserved_state[dim + 1] = p / ScalarNumber(5. / 3. - 1.) +\n                                       ScalarNumber(0.5) * rho * u * u;\n        }\n\n        return conserved_state;\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n    };\n  } // namespace EulerInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/initial_state_library.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 by the ryujin authors\n//\n\n#include \"initial_state_library.template.h\"\n\nnamespace ryujin\n{\n  template class InitialStateLibrary<Description, 1, NUMBER>;\n  template class InitialStateLibrary<Description, 2, NUMBER>;\n  template class InitialStateLibrary<Description, 3, NUMBER>;\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/initial_state_library.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include \"description.h\"\n\n#include \"initial_state_library_euler.h\"\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  using Description = Euler::Description;\n\n  template <int dim, typename Number>\n  class InitialStateLibrary<Description, dim, Number>\n  {\n  public:\n    using HyperbolicSystem = typename Description::HyperbolicSystem;\n    using ParabolicSystem = typename Description::ParabolicSystem;\n\n    using View =\n        typename Description::template HyperbolicSystemView<dim, Number>;\n\n    using initial_state_list_type =\n        std::set<std::unique_ptr<InitialState<Description, dim, Number>>>;\n\n    static void\n    populate_initial_state_list(initial_state_list_type &initial_state_list,\n                                const HyperbolicSystem &h,\n                                const ParabolicSystem & /*p*/,\n                                const std::string &s)\n    {\n      EulerInitialStates::populate_initial_state_list<Description, dim, Number>(\n          initial_state_list, h, s);\n    }\n  };\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/initial_state_library_euler.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\n#include \"initial_state_astro_jet.h\"\n#include \"initial_state_becker_solution.h\"\n#include \"initial_state_contrast.h\"\n#include \"initial_state_exact_riemann_solution.h\"\n#include \"initial_state_four_state_contrast.h\"\n#include \"initial_state_function.h\"\n#include \"initial_state_icf_like.h\"\n#include \"initial_state_isentropic_vortex.h\"\n#include \"initial_state_leblanc.h\"\n#include \"initial_state_noh.h\"\n#include \"initial_state_radial_contrast.h\"\n#include \"initial_state_ramp_up.h\"\n#include \"initial_state_rarefaction.h\"\n#include \"initial_state_shock_front.h\"\n#include \"initial_state_smooth_wave.h\"\n#include \"initial_state_three_state_contrast.h\"\n#include \"initial_state_uniform.h\"\n\nnamespace ryujin\n{\n  namespace EulerInitialStates\n  {\n    template <typename Description, int dim, typename Number>\n    void populate_initial_state_list(\n        typename ryujin::InitialStateLibrary<Description, dim, Number>::\n            initial_state_list_type &initial_state_list,\n        const typename Description::HyperbolicSystem &h,\n        const std::string &s)\n    {\n      using std::make_unique;\n\n      auto add = [&](auto &&object) {\n        initial_state_list.emplace(std::move(object));\n      };\n\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<1, double>;\n\n      add(make_unique<AstroJet<Description, dim, Number>>(h, s));\n      add(make_unique<BeckerSolution<Description, dim, Number>>(h, s));\n      add(make_unique<Contrast<Description, dim, Number>>(h, s));\n      add(make_unique<ExactRiemannSolution<Description, dim, Number>>(h, s));\n      add(make_unique<FourStateContrast<Description, dim, Number>>(h, s));\n      add(make_unique<Function<Description, dim, Number>>(h, s));\n      add(make_unique<ICFLike<Description, dim, Number>>(h, s));\n      add(make_unique<IsentropicVortex<Description, dim, Number>>(h, s));\n      add(make_unique<LeBlanc<Description, dim, Number>>(h, s));\n      add(make_unique<Noh<Description, dim, Number>>(h, s));\n      add(make_unique<RadialContrast<Description, dim, Number>>(h, s));\n      add(make_unique<RampUp<Description, dim, Number>>(h, s));\n      add(make_unique<Rarefaction<Description, dim, Number>>(h, s));\n      add(make_unique<ShockFront<Description, dim, Number>>(h, s));\n      add(make_unique<SmoothWave<Description, dim, Number>>(h, s));\n      add(make_unique<ThreeStateContrast<Description, dim, Number>>(h, s));\n      add(make_unique<Uniform<Description, dim, Number>>(h, s));\n    }\n  } // namespace EulerInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/initial_state_noh.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  namespace EulerInitialStates\n  {\n    /**\n     * The Noh problem\n     *\n     * This initial state sets up the classical Noh problem introduced in\n     * @cite Noh1987\n     *\n     * @note This class returns the analytic solution as a function of time\n     * @p t and position @p x.\n     *\n     * @ingroup EulerEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class Noh : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n\n      Noh(const HyperbolicSystem &hyperbolic_system,\n          const std::string &subsection)\n          : InitialState<Description, dim, Number>(\"noh\", subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n        gamma_ = 1.4;\n        if constexpr (!View::have_gamma) {\n          this->add_parameter(\"gamma\", gamma_, \"The ratio of specific heats\");\n        }\n\n        rho0_ = 1.0;\n        this->add_parameter(\n            \"reference density\", rho0_, \"The reference density\");\n\n        /*\n         * Exact solution assumes this value is negative, but we are just\n         * switching u0 to -u0 by hand in the formulas.\n         */\n        u0_ = 1.0;\n        this->add_parameter(\"reference velocity magnitude\",\n                            u0_,\n                            \"The reference velocity magnitude\");\n\n\n        p0_ = 1.e-12;\n        this->add_parameter(\n            \"reference pressure\", p0_, \"The reference pressure\");\n\n        this->parse_parameters_call_back.connect([&]() {\n          if constexpr (View::have_gamma) {\n            const auto view = hyperbolic_system_.template view<dim, Number>();\n            gamma_ = view.gamma();\n          }\n        });\n      }\n\n      /* Compute solution */\n      auto compute(const dealii::Point<dim> &point, Number t)\n          -> state_type final\n      {\n        const auto view = hyperbolic_system_.template view<dim, Number>();\n\n        const auto norm = point.norm();\n        const auto min = 10. * std::numeric_limits<Number>::min();\n\n        /* Initialize primitive variables */\n        Number rho = rho0_;\n        auto vel = -u0_ * point / (norm + min);\n        Number p = p0_;\n\n        /* Define exact solutions */\n        const auto D = u0_ * (gamma_ - 1.) / 2.;\n        const bool in_interior = t == Number(0.) ? false : norm / t < D;\n\n        if (in_interior) {\n          rho = rho0_ * std::pow((gamma_ + 1.) / (gamma_ - 1.), dim);\n          vel = 0. * point;\n          p = 0.5 * rho0_ * u0_ * u0_;\n          p *= std::pow(gamma_ + 1., dim) / std::pow(gamma_ - 1., dim - 1);\n        } else {\n          rho = rho0_ * std::pow(1. + t / (norm + min), dim - 1);\n        }\n\n        /* Assemble final state: */\n        state_type result;\n        result[0] = rho;\n        result[1] = Number(vel[0]);\n        if constexpr (dim >= 2)\n          result[2] = Number(vel[1]);\n        if constexpr (dim >= 3)\n          result[3] = Number(vel[2]);\n        if constexpr (View::have_energy_equation)\n          result[dim + 1] = p;\n\n        return view.from_initial_state(result);\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n      Number gamma_;\n      Number rho0_;\n      Number u0_;\n      Number p0_;\n    };\n  } // namespace EulerInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/initial_state_radial_contrast.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2022 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  namespace EulerInitialStates\n  {\n    /**\n     * A modification of the \"contrast\" initial state. Now, we have an\n     * initial state formed by a contrast of a given \"left\" and \"right\"\n     * primitive state where the \"left\" state is \"inside\" the radius, R,\n     * and the \"right\" state is outside.\n     *\n     * @note The @p t argument is ignored. This class always returns the\n     * initial configuration.\n     *\n     * @todo Transform a nonzero initial velocity into a radial momentum\n     * scaling with 1 / r^(dim -1).\n     *\n     * @ingroup EulerEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class RadialContrast : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n      using state_type_1d = typename Description::\n          template HyperbolicSystemView<1, Number>::state_type;\n\n      RadialContrast(const HyperbolicSystem &hyperbolic_system,\n                     const std::string &subsection)\n          : InitialState<Description, dim, Number>(\"radial contrast\",\n                                                   subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n        primitive_inner_[0] = 1.4;\n        primitive_inner_[1] = 0.0;\n        if constexpr (View::have_energy_equation)\n          primitive_inner_[2] = 1.;\n        this->add_parameter(\"primitive state inner\",\n                            primitive_inner_,\n                            \"1d primitive state [rho, u, p] on the inner disk \"\n                            \"(or [rho, u] for the barotropic Euler module)\");\n\n        primitive_outer_[0] = 1.4;\n        primitive_outer_[1] = 0.0;\n        if constexpr (View::have_energy_equation)\n          primitive_outer_[2] = 1.;\n        this->add_parameter(\n            \"primitive state outer\",\n            primitive_outer_,\n            \"1d primitive state [rho, u, p] on the outer annulus (or [rho, u] \"\n            \"for the barotropic Euler module)\");\n\n        radius_ = 0.5;\n        this->add_parameter(\"radius\", radius_, \"Radius of radial area\");\n\n        const auto convert_states = [&]() {\n          const auto view = hyperbolic_system_.template view<dim, Number>();\n          state_inner_ = view.from_initial_state(primitive_inner_);\n          state_outer_ = view.from_initial_state(primitive_outer_);\n        };\n        this->parse_parameters_call_back.connect(convert_states);\n        convert_states();\n      }\n\n      auto compute(const dealii::Point<dim> &point, Number /*t*/)\n          -> state_type final\n      {\n        return (point.norm() > radius_ ? state_outer_ : state_inner_);\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n      double radius_;\n\n      state_type_1d primitive_inner_;\n      state_type_1d primitive_outer_;\n\n      state_type state_inner_;\n      state_type state_outer_;\n    };\n  } // namespace EulerInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/initial_state_ramp_up.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2022 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  namespace EulerInitialStates\n  {\n    /**\n     * A time-dependent state given by an initial state @p primite_left_\n     * valid for \\f$ t \\le t_{\\text{left}} \\f$ and a final state @p\n     * primite_right_ valid for \\f$ t \\ge t_{\\text{right}} \\f$. In between,\n     * a smooth interpolation is performed.\n     *\n     * @ingroup EulerEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class RampUp : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n      using state_type_1d = typename Description::\n          template HyperbolicSystemView<1, Number>::state_type;\n\n      RampUp(const HyperbolicSystem &hyperbolic_system,\n             const std::string subsection)\n          : InitialState<Description, dim, Number>(\"ramp up\", subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n        t_initial_ = 0.;\n        this->add_parameter(\"time initial\",\n                            t_initial_,\n                            \"Time until which initial state is prescribed\");\n\n        t_final_ = 1.;\n        this->add_parameter(\"time final\",\n                            t_final_,\n                            \"Time from which on the final state is attained)\");\n\n        primitive_initial_[0] = 1.4;\n        primitive_initial_[1] = 0.0;\n        if constexpr (View::have_energy_equation)\n          primitive_initial_[2] = 1.;\n        this->add_parameter(\"primitive state initial\",\n                            primitive_initial_,\n                            \"Initial 1d primitive state [rho, u, p] (or [rho, \"\n                            \"u] for the barotropic Euler module)\");\n\n        primitive_final_[0] = 1.4;\n        primitive_final_[1] = 3.0;\n        if constexpr (View::have_energy_equation)\n          primitive_final_[2] = 1.;\n        this->add_parameter(\"primitive state final\",\n                            primitive_final_,\n                            \"Final 1d primitive state [rho, u, p] (or [rho, u] \"\n                            \"for the barotropic Euler module)\");\n\n        const auto convert_states = [&]() {\n          const auto view = hyperbolic_system_.template view<dim, Number>();\n          state_initial_ = view.from_initial_state(primitive_initial_);\n          state_final_ = view.from_initial_state(primitive_final_);\n        };\n        this->parse_parameters_call_back.connect(convert_states);\n        convert_states();\n      }\n\n      auto compute(const dealii::Point<dim> & /*point*/, Number t)\n          -> state_type final\n      {\n        state_type result;\n\n        if (t <= t_initial_) {\n          result = state_initial_;\n        } else if (t >= t_final_) {\n          result = state_final_;\n        } else {\n          const Number factor =\n              std::cos(0.5 * M_PI * (t - t_initial_) / (t_final_ - t_initial_));\n\n          const Number alpha = factor * factor;\n          const Number beta = Number(1.) - alpha;\n          result = alpha * state_initial_ + beta * state_final_;\n        }\n\n        return result;\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n      Number t_initial_;\n      Number t_final_;\n\n      state_type_1d primitive_initial_;\n      state_type_1d primitive_final_;\n\n      state_type state_initial_;\n      state_type state_final_;\n    };\n  } // namespace EulerInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/initial_state_rarefaction.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  namespace EulerInitialStates\n  {\n    /**\n     * An Analytic solution for the compressible Euler equations with\n     * polytropic gas equation of state consisting of a smooth rarefaction\n     * wave. This analytic solution is discussed in detail in Section 5.3\n     * of @cite GuermondEtAl2018\n     *\n     * @note This class returns the analytic solution as a function of time\n     * @p t and position @p x.\n     *\n     * @ingroup EulerEquations\n     */\n\n    template <typename Description, int dim, typename Number>\n    class Rarefaction : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n\n      using state_type_1d = std::array<Number, 4>;\n\n      Rarefaction(const HyperbolicSystem &hyperbolic_system,\n                  const std::string subsection)\n          : InitialState<Description, dim, Number>(\"rarefaction\", subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n        gamma_ = 1.4;\n        if constexpr (!View::have_gamma) {\n          this->add_parameter(\"gamma\", gamma_, \"The ratio of specific heats\");\n        }\n\n        /*\n         * Compute the speed of sound:\n         */\n        const auto speed_of_sound = [&](const Number rho, const Number p) {\n          return std::sqrt(gamma_ * p / rho);\n        };\n\n        /*\n         * Compute the rarefaction right side:\n         */\n        const auto rarefaction_right_state = [this, speed_of_sound](\n                                                 const auto primitive_left,\n                                                 const Number rho_right) {\n          const auto &[rho_left, u_left, p_left, c_left] = primitive_left;\n          state_type_1d primitive_right{{rho_right, 0., 0.}};\n\n          /* Isentropic condition: pR = (rhoR/rhoL)^{gamma} * pL */\n          primitive_right[2] = std::pow(rho_right / rho_left, gamma_) * p_left;\n\n          const auto c_right = speed_of_sound(rho_right, primitive_right[2]);\n          primitive_right[3] = c_right;\n\n          /* 1-Riemann invariant: uR + 2 cR/(gamma -1) = uL + 2 cL/(gamma -1) */\n          primitive_right[1] =\n              u_left + 2.0 * (c_left - c_right) / (gamma_ - 1.0);\n\n          return primitive_right;\n        };\n\n        const auto compute_constants =\n            [this, speed_of_sound, rarefaction_right_state]() {\n              const auto view = hyperbolic_system_.template view<dim, Number>();\n              if constexpr (View::have_gamma) {\n                gamma_ = view.gamma();\n              }\n\n              /*\n               * Initial left and right states (rho, u, p, c):\n               */\n\n              const Number rho_left = 3.0;\n              const Number p_left = 1.0;\n              const Number c_left = speed_of_sound(rho_left, p_left);\n              const Number u_left = c_left; /* verify */\n              const Number rho_right = 0.5;\n\n              primitive_left_ = {rho_left, c_left, p_left, c_left};\n              primitive_right_ =\n                  rarefaction_right_state(primitive_left_, rho_right);\n\n              /*\n               * Populate constants:\n               */\n\n              k1 = 2.0 / (gamma_ + 1.0);\n              k2 = ((gamma_ - 1.0) / ((gamma_ + 1.0) * c_left));\n              density_exponent = 2.0 / (gamma_ - 1.0);\n              k3 = c_left + ((gamma_ - 1.0) / 2.0) * u_left;\n              pressure_exponent = 2.0 * gamma_ / (gamma_ - 1.0);\n            };\n\n        this->parse_parameters_call_back.connect(compute_constants);\n        compute_constants();\n      } /* Constructor */\n\n      state_type compute(const dealii::Point<dim> &point, Number delta_t) final\n      {\n        /*\n         * Compute rarefaction solution:\n         */\n\n        const auto &[rho_left, u_left, p_left, c_left] = primitive_left_;\n        const auto &[rho_right, u_right, p_right, c_right] = primitive_right_;\n\n        const double x = point[0];\n        const auto t_0 = 0.2 / (u_right - u_left);\n        const auto t = t_0 + delta_t;\n\n        state_type_1d primitive;\n\n        if (x <= t * (u_left - c_left)) {\n          primitive = primitive_left_;\n\n        } else if (x <= t * (u_right - c_right)) {\n\n          /* Self-similar variable: */\n          const double chi = x / t;\n\n          primitive[0] =\n              rho_left * std::pow(k1 + k2 * (u_left - chi), density_exponent);\n          primitive[1] = k1 * (k3 + chi);\n          primitive[2] =\n              p_left * std::pow(k1 + k2 * (u_left - chi), pressure_exponent);\n\n        } else {\n          primitive = primitive_right_;\n        }\n\n        state_type conserved_state;\n        {\n          const auto &[rho, u, p, c] = primitive;\n          conserved_state[0] = rho;\n          conserved_state[1] = rho * u;\n          if constexpr (View::have_energy_equation)\n            conserved_state[dim + 1] =\n                p / Number(gamma_ - 1.) + Number(0.5) * rho * u * u;\n        }\n        return conserved_state;\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n      Number gamma_;\n\n      state_type_1d primitive_left_;\n      state_type_1d primitive_right_;\n      Number k1;\n      Number k2;\n      Number density_exponent;\n      Number k3;\n      Number pressure_exponent;\n    };\n  } // namespace EulerInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/initial_state_shock_front.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2022 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  namespace EulerInitialStates\n  {\n    /**\n     * The S1/S3 shock front.\n     *\n     * An Analytic solution for the compressible Euler equations with\n     * polytropic gas equation of state consisting of a shock front\n     * evolving in time.\n     *\n     * @note This class returns the analytic solution as a function of time\n     * @p t and position @p x.\n     *\n     * @ingroup EulerEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class ShockFront : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n\n      ShockFront(const HyperbolicSystem &hyperbolic_system,\n                 const std::string subsection)\n          : InitialState<Description, dim, Number>(\"shock front\", subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n        gamma_ = 1.4;\n        if constexpr (!View::have_gamma) {\n          this->add_parameter(\"gamma\", gamma_, \"The ratio of specific heats\");\n        }\n\n        primitive_right_[0] = 1.4;\n        primitive_right_[1] = 0.0;\n        primitive_right_[2] = 1.;\n        this->add_parameter(\n            \"primitive state\",\n            primitive_right_,\n            \"1d primitive state [rho, u, p] (for the Noble-Abel gas EOS) \"\n            \"before the shock (to the right)\");\n\n        mach_number_ = 2.0;\n        this->add_parameter(\n            \"mach number\",\n            mach_number_,\n            \"Mach number of shock front (S1, S3 = mach * a_L/R)\");\n\n        const auto compute_and_convert_states = [&]() {\n          const auto view = hyperbolic_system_.template view<dim, Number>();\n\n          if constexpr (View::have_gamma) {\n            gamma_ = view.gamma();\n          }\n\n          /* Compute post-shock state and S3: */\n\n          auto b = Number(0.);\n          if constexpr (View::have_eos_interpolation_b)\n            b = view.eos_interpolation_b();\n\n          const auto &rho_R = primitive_right_[0];\n          const auto &u_R = primitive_right_[1];\n          const auto &p_R = primitive_right_[2];\n          /* a_R^2 = gamma * p / rho / (1 - b * rho) */\n          const Number a_R = std::sqrt(gamma_ * p_R / rho_R / (1 - b * rho_R));\n          const Number mach_R = u_R / a_R;\n\n          S3_ = mach_number_ * a_R;\n          const Number delta_mach = mach_R - mach_number_;\n\n          const Number rho_L =\n              rho_R * (gamma_ + Number(1.)) * delta_mach * delta_mach /\n              ((gamma_ - Number(1.)) * delta_mach * delta_mach + Number(2.));\n          const Number u_L =\n              (Number(1.) - rho_R / rho_L) * S3_ + rho_R / rho_L * u_R;\n          const Number p_L = p_R *\n                             (Number(2.) * gamma_ * delta_mach * delta_mach -\n                              (gamma_ - Number(1.))) /\n                             (gamma_ + Number(1.));\n\n          using state_type_1d = typename Description::\n              template HyperbolicSystemView<1, Number>::state_type;\n\n          if constexpr (View::have_energy_equation) {\n            state_left_ =\n                view.from_initial_state(state_type_1d{{rho_L, u_L, p_L}});\n            state_right_ =\n                view.from_initial_state(state_type_1d{{rho_R, u_R, p_R}});\n          } else {\n            state_left_ = view.from_initial_state(state_type_1d{{rho_L, u_L}});\n            state_right_ = view.from_initial_state(state_type_1d{{rho_R, u_R}});\n          }\n        };\n\n        this->parse_parameters_call_back.connect(compute_and_convert_states);\n        compute_and_convert_states();\n      }\n\n\n      state_type compute(const dealii::Point<dim> &point, Number t) final\n      {\n        const Number position_1d = Number(point[0] - S3_ * t);\n        return (position_1d > 0. ? state_right_ : state_left_);\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n      Number gamma_;\n\n      dealii::Tensor<1, 3, Number> primitive_left_;\n      dealii::Tensor<1, 3, Number> primitive_right_;\n      Number mach_number_;\n      Number S3_;\n\n      state_type state_left_;\n      state_type state_right_;\n    };\n  } // namespace EulerInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/initial_state_smooth_wave.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n#include <simd.h>\n\nnamespace ryujin\n{\n  namespace EulerInitialStates\n  {\n    /**\n     * This is a generalization of the \"Smooth traveling wave\" problem first\n     * proposed in Section 5.2 of @cite GuermondEtAl2018 for ideal gas EOS.\n     * The details for extending to arbitrary equations of state are seen in\n     * Section 5.3.1 of @cite ryujin-2023-4.\n     *\n     * @note This class returns the analytic solution as a function of time\n     * @p t and position @p x.\n     *\n     * @ingroup EulerEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class SmoothWave : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n      using state_type_1d = typename Description::\n          template HyperbolicSystemView<1, Number>::state_type;\n\n      using ScalarNumber = typename View::ScalarNumber;\n\n      SmoothWave(const HyperbolicSystem &hyperbolic_system,\n                 const std::string subsection)\n          : InitialState<Description, dim, Number>(\"smooth wave\", subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n        density_ref_ = 1.;\n        this->add_parameter(\"reference density\",\n                            density_ref_,\n                            \"The material reference density\");\n\n        pressure_ref_ = 1.;\n        this->add_parameter(\"reference pressure\",\n                            pressure_ref_,\n                            \"The material reference pressure\");\n\n        mach_number_ = 1.0;\n        this->add_parameter(\"mach number\",\n                            mach_number_,\n                            \"Mach number of traveling smooth wave\");\n\n        /* These are the x_0 and x_1 parameters from references above. */\n        left_ = 0.1;\n        right_ = 0.3;\n      }\n\n      state_type compute(const dealii::Point<dim> &point, Number t) final\n      {\n        const auto view = hyperbolic_system_.template view<dim, Number>();\n\n        auto point_bar = point;\n        point_bar[0] = point_bar[0] - mach_number_ * t;\n        const auto x = Number(point_bar[0]);\n\n        const Number polynomial = Number(64) *\n                                  ryujin::fixed_power<3>(x - left_) *\n                                  ryujin::fixed_power<3>(right_ - x) /\n                                  ryujin::fixed_power<6>(right_ - left_);\n\n        /* Define density profile */\n        Number rho = density_ref_;\n        if (left_ <= point_bar[0] && point_bar[0] <= right_)\n          rho = density_ref_ + polynomial;\n\n        state_type_1d initial_state;\n        initial_state[0] = rho;\n        initial_state[1] = mach_number_;\n        if constexpr (View::have_energy_equation)\n          initial_state[2] = pressure_ref_;\n        return view.from_initial_state(initial_state);\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n      Number density_ref_;\n      Number pressure_ref_;\n      Number mach_number_;\n      Number left_;\n      Number right_;\n    };\n  } // namespace EulerInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/initial_state_three_state_contrast.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  namespace EulerInitialStates\n  {\n    /**\n     * An initial state formed by two contrasts of given \"left\", \"middle\"\n     * and \"right\" primitive states. The user defines the lengths of the left\n     * and middle regions. The rest of the domain is populated with the right\n     * region. This initial state (default values) can be used to replicate\n     * the classical Woodward-Colella colliding blast wave problem described\n     * in @cite Woodward1984\n     *\n     * @note The @p t argument is ignored. This class always returns the\n     * initial configuration.\n     *\n     * @ingroup EulerEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class ThreeStateContrast : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n      using state_type_1d = typename Description::\n          template HyperbolicSystemView<1, Number>::state_type;\n\n      ThreeStateContrast(const HyperbolicSystem &hyperbolic_system,\n                         const std::string &subsection)\n          : InitialState<Description, dim, Number>(\"three state contrast\",\n                                                   subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n\n        primitive_left_[0] = 1.;\n        primitive_left_[1] = 0.;\n        if constexpr (View::have_energy_equation)\n          primitive_left_[2] = 1.e3;\n        this->add_parameter(\"primitive state left\",\n                            primitive_left_,\n                            \"1d primitive state [rho, u, p] on the left (or \"\n                            \"[rho, u] for the barotropic Euler module)\");\n\n        left_length_ = 0.1;\n        this->add_parameter(\"left region length\",\n                            left_length_,\n                            \"The length of the left region\");\n\n        primitive_middle_[0] = 1.;\n        primitive_middle_[1] = 0.;\n        if constexpr (View::have_energy_equation)\n          primitive_middle_[2] = 1.e-2;\n        this->add_parameter(\"primitive state middle\",\n                            primitive_middle_,\n                            \"1d primitive state [rho, u, p] in the middle (or \"\n                            \"[rho, u] for the barotropic Euler module)\");\n\n        middle_length_ = 0.8;\n        this->add_parameter(\"middle region length\",\n                            middle_length_,\n                            \"The length of the middle region\");\n\n        primitive_right_[0] = 1.;\n        primitive_right_[1] = 0.;\n        if constexpr (View::have_energy_equation)\n          primitive_right_[2] = 1.e2;\n        this->add_parameter(\"primitive state right\",\n                            primitive_right_,\n                            \"1d primitive state [rho, u, p] on the right (or \"\n                            \"[rho, u] for the barotropic Euler module)\");\n\n        const auto convert_states = [&]() {\n          const auto view = hyperbolic_system_.template view<dim, Number>();\n          state_left_ = view.from_initial_state(primitive_left_);\n          state_middle_ = view.from_initial_state(primitive_middle_);\n          state_right_ = view.from_initial_state(primitive_right_);\n        };\n        this->parse_parameters_call_back.connect(convert_states);\n        convert_states();\n      }\n\n      state_type compute(const dealii::Point<dim> &point, Number /*t*/) final\n      {\n        return point[0] >= left_length_ + middle_length_ ? state_right_\n               : point[0] >= left_length_                ? state_middle_\n                                                         : state_left_;\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n      Number left_length_;\n      Number middle_length_;\n\n      state_type_1d primitive_left_;\n      state_type_1d primitive_middle_;\n      state_type_1d primitive_right_;\n\n      state_type state_left_;\n      state_type state_middle_;\n      state_type state_right_;\n    };\n  } // namespace EulerInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/initial_state_uniform.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2022 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  namespace EulerInitialStates\n  {\n    /**\n     * Returns a uniform initial state defined by a given primitive\n     * (initial) state.\n     *\n     * @note The @p t argument is ignored. This class always returns the\n     * initial configuration.\n     *\n     * @ingroup EulerEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class Uniform : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n      using state_type_1d = typename Description::\n          template HyperbolicSystemView<1, Number>::state_type;\n\n      Uniform(const HyperbolicSystem &hyperbolic_system,\n              const std::string subsection)\n          : InitialState<Description, dim, Number>(\"uniform\", subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n        primitive_[0] = 1.4;\n        primitive_[1] = 3.;\n        if constexpr (View::have_energy_equation)\n          primitive_[2] = 1.;\n        this->add_parameter(\"primitive state\",\n                            primitive_,\n                            \"1d primitive state [rho, u, p] (or [rho, u] for \"\n                            \"the barotropic Euler module)\");\n\n        const auto convert_states = [&]() {\n          const auto view = hyperbolic_system_.template view<dim, Number>();\n          state_ = view.from_initial_state(primitive_);\n        };\n        this->parse_parameters_call_back.connect(convert_states);\n        convert_states();\n      }\n\n      state_type compute(const dealii::Point<dim> & /*point*/,\n                         Number /*t*/) final\n      {\n        return state_;\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n      state_type_1d primitive_;\n\n      state_type state_;\n    };\n  } // namespace EulerInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/instantiate.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 by the ryujin authors\n//\n\n#ifndef RYUJIN_INCLUDE_INSTANTIATION_ONCE\n#define RYUJIN_INCLUDE_INSTANTIATION_ONCE\n#else\n#error Instantiation files can only be included once.\n#endif\n\n#include \"description.h\"\n\nnamespace ryujin\n{\n  using Euler::Description;\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/limiter.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2023 by the ryujin authors\n//\n\n#include \"limiter.template.h\"\n\nusing namespace dealii;\n\nnamespace ryujin\n{\n  namespace Euler\n  {\n    /* instantiations */\n\n    template class Limiter<1, NUMBER>;\n    template class Limiter<2, NUMBER>;\n    template class Limiter<3, NUMBER>;\n\n    template class Limiter<1, dealii::VectorizedArray<NUMBER>>;\n    template class Limiter<2, dealii::VectorizedArray<NUMBER>>;\n    template class Limiter<3, dealii::VectorizedArray<NUMBER>>;\n  } // namespace Euler\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/limiter.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"hyperbolic_system.h\"\n\n#include <compile_time_options.h>\n#include <deal.II/base/config.h>\n#include <multicomponent_vector.h>\n#include <newton.h>\n#include <simd.h>\n\nnamespace ryujin\n{\n  namespace Euler\n  {\n    template <typename ScalarNumber = double>\n    class LimiterParameters : public dealii::ParameterAcceptor\n    {\n    public:\n      LimiterParameters(const std::string &subsection = \"/Limiter\")\n          : ParameterAcceptor(subsection)\n      {\n        iterations_ = 2;\n        add_parameter(\n            \"iterations\", iterations_, \"Number of limiter iterations\");\n\n        if constexpr (std::is_same<ScalarNumber, double>::value)\n          newton_tolerance_ = 1.e-10;\n        else\n          newton_tolerance_ = 1.e-4;\n        add_parameter(\"newton tolerance\",\n                      newton_tolerance_,\n                      \"Tolerance for the quadratic newton stopping criterion\");\n\n        newton_max_iterations_ = 2;\n        add_parameter(\"newton max iterations\",\n                      newton_max_iterations_,\n                      \"Maximal number of quadratic newton iterations performed \"\n                      \"during limiting\");\n\n        relaxation_factor_ = ScalarNumber(1.);\n        add_parameter(\"relaxation factor\",\n                      relaxation_factor_,\n                      \"Factor for scaling the relaxation window with r_i = \"\n                      \"factor * (m_i/|Omega|)^(1.5/d).\");\n      }\n\n      ACCESSOR_READ_ONLY(iterations);\n      ACCESSOR_READ_ONLY(newton_tolerance);\n      ACCESSOR_READ_ONLY(newton_max_iterations);\n      ACCESSOR_READ_ONLY(relaxation_factor);\n\n    private:\n      unsigned int iterations_;\n      ScalarNumber newton_tolerance_;\n      unsigned int newton_max_iterations_;\n      ScalarNumber relaxation_factor_;\n    };\n\n\n    /**\n     * The convex limiter.\n     *\n     * The class implements a convex limiting technique as described in\n     * @cite GuermondEtAl2018 and @cite ryujin-2021-1. Given a\n     * computed set of bounds and an update direction \\f$\\mathbf P_{ij}\\f$\n     * one can now determine a candidate \\f$\\tilde l_{ij}\\f$ by computing\n     *\n     * \\f{align}\n     *   \\tilde l_{ij} = \\max_{l\\,\\in\\,[0,1]}\n     *   \\,\\Big\\{\\rho_{\\text{min}}\\,\\le\\,\\rho\\,(\\mathbf U_i +\\tilde\n     * l_{ij}\\mathbf P_{ij})\n     *   \\,\\le\\,\\rho_{\\text{max}},\\quad\n     *   \\phi_{\\text{min}}\\,\\le\\,\\phi\\,(\\mathbf U_{i}+\\tilde l_{ij}\\mathbf\n     * P_{ij})\\Big\\}, \\f}\n     *\n     * where \\f$\\psi\\f$ denots the specific entropy @cite ryujin-2021-1.\n     *\n     * Algorithmically this is accomplished as follows: Given an initial\n     * interval \\f$[t_L,t_R]\\f$, where \\f$t_L\\f$ is a good state, we first\n     * make the interval smaller ensuring the bounds on the density are\n     * fulfilled. If limiting on the specific entropy is selected we then\n     * then perform a quadratic Newton iteration (updating \\f$[t_L,t_R]\\f$\n     * solving for the root of a 3-convex function\n     * \\f{align}\n     *     \\Psi(\\mathbf U)\\;=\\;\\rho^{\\gamma+1}(\\mathbf U)\\,\\big(\\phi(\\mathbf\n     * U)-\\phi_{\\text{min}}\\big). \\f}\n     *\n     * @ingroup EulerEquations\n     */\n    template <int dim, typename Number = double>\n    class Limiter\n    {\n    public:\n      /**\n       * @name Typedefs and constexpr constants\n       */\n      //@{\n\n      using View = HyperbolicSystemView<dim, Number>;\n\n      using ScalarNumber = typename View::ScalarNumber;\n\n      static constexpr auto problem_dimension = View::problem_dimension;\n\n      using state_type = typename View::state_type;\n\n      using flux_contribution_type = typename View::flux_contribution_type;\n\n      using precomputed_type = typename View::precomputed_type;\n\n      using PrecomputedVector = typename View::PrecomputedVector;\n\n      using Parameters = LimiterParameters<ScalarNumber>;\n\n      //@}\n      /**\n       * @name Computation and manipulation of bounds\n       */\n      //\n      //@{\n      /**\n       * The number of stored entries in the bounds array.\n       */\n      static constexpr unsigned int n_bounds = 3;\n\n      /**\n       * Array type used to store accumulated bounds.\n       */\n      using Bounds = std::array<Number, n_bounds>;\n\n      /**\n       * Constructor taking a HyperbolicSystem instance as argument\n       */\n      Limiter(const HyperbolicSystem &hyperbolic_system,\n              const Parameters &parameters,\n              const PrecomputedVector &precomputed_values)\n          : hyperbolic_system(hyperbolic_system)\n          , parameters(parameters)\n          , precomputed_values(precomputed_values)\n      {\n      }\n\n      /**\n       * Given a state @p U_i and an index @p i return \"strict\" bounds,\n       * i.e., a minimal convex set containing the state.\n       */\n      Bounds projection_bounds_from_state(const unsigned int i,\n                                          const state_type &U_i) const;\n\n      /**\n       * Given two bounds bounds_left, bounds_right, this function computes\n       * a larger, combined set of bounds that this is a (convex) superset\n       * of the two.\n       */\n      Bounds combine_bounds(const Bounds &bounds_left,\n                            const Bounds &bounds_right) const;\n\n      /**\n       * This function applies a relaxation to a given a (strict) bound @p\n       * bounds using a non dimensionalized measure @p hd (that should\n       * scale as $h^d$, where $h$ is the local mesh size). This is done\n       * for the case of the Euler equations by multiplying maximum bounds\n       * with $(1+r)$ and minimum bounds with $(1-r)$, while ensuring that\n       * the bounds still describe an admissible state.\n       */\n      Bounds fully_relax_bounds(const Bounds &bounds, const Number &hd) const;\n\n      //@}\n      /**\n       * @name Stencil-based computation of bounds\n       *\n       * Intended usage:\n       * ```\n       * Limiter<dim, Number> limiter;\n       * for (unsigned int i = n_internal; i < n_owned; ++i) {\n       *   // ...\n       *   limiter.reset(i, U_i, flux_i);\n       *   for (unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {\n       *     // ...\n       *     limiter.accumulate(js, U_j, flux_j, scaled_c_ij, affine_shift);\n       *   }\n       *   limiter.bounds(hd_i);\n       * }\n       * ```\n       */\n      //@{\n\n      /**\n       * Reset temporary storage\n       */\n      void reset(const unsigned int i,\n                 const state_type &U_i,\n                 const flux_contribution_type &flux_i);\n\n      /**\n       * When looping over the sparsity row, add the contribution associated\n       * with the neighboring state U_j.\n       */\n      void accumulate(const unsigned int *js,\n                      const state_type &U_j,\n                      const flux_contribution_type &flux_j,\n                      const dealii::Tensor<1, dim, Number> &scaled_c_ij,\n                      const state_type &affine_shift);\n\n      /**\n       * Return the computed bounds (with relaxation applied).\n       */\n      Bounds bounds(const Number hd_i) const;\n\n      //*}\n      /** @name Convex limiter */\n      //@{\n\n      /**\n       * Given a state \\f$\\mathbf U\\f$ and an update \\f$\\mathbf P\\f$ this\n       * function computes and returns the maximal coefficient \\f$t\\f$,\n       * obeying \\f$t_{\\text{min}} < t < t_{\\text{max}}\\f$, such that the\n       * selected local minimum principles are obeyed.\n       *\n       * The returned boolean is set to true if the original low-order\n       * update was within bounds.\n       *\n       * @note If the debug option `DEBUG_EXPENSIVE_BOUNDS_CHECK` is set to\n       * true, then the boolean is set to true if the low-order and the\n       * resulting high-order update are within bounds. The latter might be\n       * violated due to round-off errors when computing the limiter\n       * bounds.\n       */\n      std::tuple<Number, bool> limit(const Bounds &bounds,\n                                     const state_type &U,\n                                     const state_type &P,\n                                     const Number t_min = Number(0.),\n                                     const Number t_max = Number(1.)) const;\n\n    private:\n      //@}\n      /** @name Arguments and internal fields */\n      //@{\n\n      const HyperbolicSystem &hyperbolic_system;\n      const Parameters &parameters;\n      const PrecomputedVector &precomputed_values;\n\n      state_type U_i;\n\n      Bounds bounds_;\n\n      Number rho_relaxation_numerator;\n      Number rho_relaxation_denominator;\n      Number s_interp_max;\n\n      //@}\n    };\n\n\n    /*\n     * -------------------------------------------------------------------------\n     * Inline definitions\n     * -------------------------------------------------------------------------\n     */\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    Limiter<dim, Number>::projection_bounds_from_state(\n        const unsigned int i, const state_type &U_i) const -> Bounds\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n      const auto rho_i = view.density(U_i);\n      const auto &[s_i, eta_i] =\n          precomputed_values.template read_tensor<Number, precomputed_type>(i);\n\n      return {/*rho_min*/ rho_i, /*rho_max*/ rho_i, /*s_min*/ s_i};\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto Limiter<dim, Number>::combine_bounds(\n        const Bounds &bounds_left, const Bounds &bounds_right) const -> Bounds\n    {\n      const auto &[rho_min_l, rho_max_l, s_min_l] = bounds_left;\n      const auto &[rho_min_r, rho_max_r, s_min_r] = bounds_right;\n\n      return {std::min(rho_min_l, rho_min_r),\n              std::max(rho_max_l, rho_max_r),\n              std::min(s_min_l, s_min_r)};\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    Limiter<dim, Number>::fully_relax_bounds(const Bounds &bounds,\n                                             const Number &hd) const -> Bounds\n    {\n      auto relaxed_bounds = bounds;\n      auto &[rho_min, rho_max, s_min] = relaxed_bounds;\n\n      /* Use r = factor * (m_i / |Omega|) ^ (1.5 / d): */\n\n      Number r = std::sqrt(hd);                              // in 3D: ^ 3/6\n      if constexpr (dim == 2)                                //\n        r = dealii::Utilities::fixed_power<3>(std::sqrt(r)); // in 2D: ^ 3/4\n      else if constexpr (dim == 1)                           //\n        r = dealii::Utilities::fixed_power<3>(r);            // in 1D: ^ 3/2\n      r *= parameters.relaxation_factor();\n\n      constexpr ScalarNumber eps = std::numeric_limits<ScalarNumber>::epsilon();\n      rho_min *= std::max(Number(1.) - r, Number(eps));\n      rho_max *= (Number(1.) + r);\n      s_min *= std::max(Number(1.) - r, Number(eps));\n\n      return relaxed_bounds;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline void\n    Limiter<dim, Number>::reset(const unsigned int /*i*/,\n                                const state_type &new_U_i,\n                                const flux_contribution_type & /*new_flux_i*/)\n    {\n      U_i = new_U_i;\n\n      /* Bounds: */\n\n      auto &[rho_min, rho_max, s_min] = bounds_;\n\n      rho_min = Number(std::numeric_limits<ScalarNumber>::max());\n      rho_max = Number(0.);\n      s_min = Number(std::numeric_limits<ScalarNumber>::max());\n\n      /* Relaxation: */\n\n      rho_relaxation_numerator = Number(0.);\n      rho_relaxation_denominator = Number(0.);\n      s_interp_max = Number(0.);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline void Limiter<dim, Number>::accumulate(\n        const unsigned int *js,\n        const state_type &U_j,\n        const flux_contribution_type & /*flux_j*/,\n        const dealii::Tensor<1, dim, Number> &scaled_c_ij,\n        const state_type &affine_shift)\n    {\n      // TODO: Currently we only apply the affine_shift to U_ij_bar (which\n      // then enters all bounds), but we do not modify s_interp and\n      // rho_relaxation. When actually adding a source term to the Euler\n      // equations verify that this does the right thing.\n      Assert(std::max(affine_shift.norm(), Number(0.)) == Number(0.),\n             dealii::ExcNotImplemented());\n\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      /* Bounds: */\n      auto &[rho_min, rho_max, s_min] = bounds_;\n\n      const auto rho_i = view.density(U_i);\n      const auto m_i = view.momentum(U_i);\n      const auto rho_j = view.density(U_j);\n      const auto m_j = view.momentum(U_j);\n      const auto rho_affine_shift = view.density(affine_shift);\n\n      /* bar state shifted by an affine shift: */\n      const auto rho_ij_bar =\n          ScalarNumber(0.5) * (rho_i + rho_j + (m_i - m_j) * scaled_c_ij) +\n          rho_affine_shift;\n\n      rho_min = std::min(rho_min, rho_ij_bar);\n      rho_max = std::max(rho_max, rho_ij_bar);\n\n      const auto &[s_j, eta_j] =\n          precomputed_values.template read_tensor<Number, precomputed_type>(js);\n      s_min = std::min(s_min, s_j);\n\n      /* Relaxation: */\n\n      /* Use a uniform weight. */\n      const auto beta_ij = Number(1.);\n      rho_relaxation_numerator += beta_ij * (rho_i + rho_j);\n      rho_relaxation_denominator += std::abs(beta_ij);\n\n      const Number s_interp =\n          view.specific_entropy((U_i + U_j) * ScalarNumber(.5));\n      s_interp_max = std::max(s_interp_max, s_interp);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    Limiter<dim, Number>::bounds(const Number hd_i) const -> Bounds\n    {\n      const auto &[rho_min, rho_max, s_min] = bounds_;\n\n      auto relaxed_bounds = fully_relax_bounds(bounds_, hd_i);\n      auto &[rho_min_relaxed, rho_max_relaxed, s_min_relaxed] = relaxed_bounds;\n\n      /* Apply a stricter window: */\n\n      constexpr ScalarNumber eps = std::numeric_limits<ScalarNumber>::epsilon();\n\n      const auto rho_relaxation =\n          ScalarNumber(2. * parameters.relaxation_factor()) *\n          std::abs(rho_relaxation_numerator) /\n          (std::abs(rho_relaxation_denominator) + Number(eps));\n\n      const auto entropy_relaxation =\n          parameters.relaxation_factor() * (s_interp_max - s_min);\n\n      rho_min_relaxed = std::max(rho_min_relaxed, rho_min - rho_relaxation);\n      rho_max_relaxed = std::min(rho_max_relaxed, rho_max + rho_relaxation);\n      s_min_relaxed = std::max(s_min_relaxed, s_min - entropy_relaxation);\n\n      return relaxed_bounds;\n    }\n  } // namespace Euler\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/limiter.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include \"limiter.h\"\n// #define DEBUG_OUTPUT_LIMITER\n\nnamespace ryujin\n{\n  namespace Euler\n  {\n    template <int dim, typename Number>\n    std::tuple<Number, bool>\n    Limiter<dim, Number>::limit(const Bounds &bounds,\n                                const state_type &U,\n                                const state_type &P,\n                                const Number t_min /* = Number(0.) */,\n                                const Number t_max /* = Number(1.) */) const\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      bool success = true;\n      Number t_r = t_max;\n\n      constexpr ScalarNumber eps = std::numeric_limits<ScalarNumber>::epsilon();\n      const auto small = view.vacuum_state_relaxation_small();\n      const auto large = view.vacuum_state_relaxation_large();\n      const ScalarNumber relax_small = ScalarNumber(1. + small * eps);\n      const ScalarNumber relax = ScalarNumber(1. + large * eps);\n\n      /*\n       * First limit the density rho.\n       *\n       * See [Guermond, Nazarov, Popov, Thomas] (4.8):\n       */\n\n      {\n        const auto &rho_U = view.density(U);\n        const auto &rho_P = view.density(P);\n\n        const auto &rho_min = std::get<0>(bounds);\n        const auto &rho_max = std::get<1>(bounds);\n\n        /*\n         * Verify that rho_U is within bounds. This property might be\n         * violated for relative CFL numbers larger than 1.\n         */\n        const auto test_min = view.filter_vacuum_density(\n            std::max(Number(0.), rho_U - relax * rho_max));\n        const auto test_max = view.filter_vacuum_density(\n            std::max(Number(0.), rho_min - relax * rho_U));\n        if (!(test_min == Number(0.) && test_max == Number(0.))) {\n#ifdef DEBUG_OUTPUT\n          std::cout << std::fixed << std::setprecision(16);\n          std::cout << \"Bounds violation: low-order density (critical)!\"\n                    << \"\\n\\t\\trho min:         \" << rho_min\n                    << \"\\n\\t\\trho min (delta): \"\n                    << negative_part(rho_U - rho_min)\n                    << \"\\n\\t\\trho:             \" << rho_U\n                    << \"\\n\\t\\trho max (delta): \"\n                    << positive_part(rho_U - rho_max)\n                    << \"\\n\\t\\trho max:         \" << rho_max << \"\\n\"\n                    << std::endl;\n#endif\n          success = false;\n        }\n\n        const Number denominator =\n            ScalarNumber(1.) / (std::abs(rho_P) + eps * rho_max);\n\n        constexpr auto lt = dealii::SIMDComparison::less_than;\n\n        t_r = dealii::compare_and_apply_mask<lt>( //\n            rho_max,\n            rho_U + t_r * rho_P,\n            /*\n             * rho_P is positive.\n             *\n             * Note: Do not take an absolute value here. If we are out of\n             * bounds we have to ensure that t_r is set to t_min.\n             */\n            (rho_max - rho_U) * denominator,\n            t_r);\n\n        t_r = dealii::compare_and_apply_mask<lt>( //\n            rho_U + t_r * rho_P,\n            rho_min,\n            /*\n             * rho_P is negative.\n             *\n             * Note: Do not take an absolute value here. If we are out of\n             * bounds we have to ensure that t_r is set to t_min.\n             */\n            (rho_U - rho_min) * denominator,\n            t_r);\n\n        /*\n         * Ensure that t_min <= t <= t_max. This might not be the case if\n         * rho_U is outside the interval [rho_min, rho_max]. Furthermore,\n         * the quotient we take above is prone to numerical cancellation in\n         * particular in the second pass of the limiter when rho_P might be\n         * small.\n         */\n        t_r = std::min(t_r, t_max);\n        t_r = std::max(t_r, t_min);\n\n#ifdef DEBUG_EXPENSIVE_BOUNDS_CHECK\n        /*\n         * Verify that the new state is within bounds:\n         */\n        const auto rho_new = view.density(U + t_r * P);\n        const auto test_new_min = view.filter_vacuum_density(\n            std::max(Number(0.), rho_new - relax * rho_max));\n        const auto test_new_max = view.filter_vacuum_density(\n            std::max(Number(0.), rho_min - relax * rho_new));\n        if (!(test_new_min == Number(0.) && test_new_max == Number(0.))) {\n#ifdef DEBUG_OUTPUT\n          std::cout << std::fixed << std::setprecision(16);\n          std::cout << \"Bounds violation: high-order density!\"\n                    << \"\\n\\t\\trho min:         \" << rho_min\n                    << \"\\n\\t\\trho min (delta): \"\n                    << negative_part(rho_new - rho_min)\n                    << \"\\n\\t\\trho:             \" << rho_new\n                    << \"\\n\\t\\trho max (delta): \"\n                    << positive_part(rho_new - rho_max)\n                    << \"\\n\\t\\trho max:         \" << rho_max << \"\\n\"\n                    << std::endl;\n#endif\n          success = false;\n        }\n#endif\n      }\n\n      /*\n       * Then limit the specific entropy:\n       *\n       * See [Guermond, Nazarov, Popov, Thomas], Section 4.6 + Section 5.1:\n       */\n\n      Number t_l = t_min; // good state\n\n      const ScalarNumber gamma = view.gamma();\n      const ScalarNumber gp1 = gamma + ScalarNumber(1.);\n\n      {\n        /*\n         * Prepare a quadratic Newton method:\n         *\n         * Given initial limiter values t_l and t_r with psi(t_l) > 0 and\n         * psi(t_r) < 0 we try to find t^\\ast with psi(t^\\ast) \\approx 0.\n         *\n         * Here, psi is a 3-convex function obtained by scaling the specific\n         * entropy s:\n         *\n         *   psi = \\rho ^ {\\gamma + 1} s\n         *\n         * (s in turn was defined as s =\\varepsilon \\rho ^{-\\gamma}, where\n         * \\varepsilon = (\\rho e) is the internal energy.)\n         */\n\n        const auto &s_min = std::get<2>(bounds);\n\n#ifdef DEBUG_OUTPUT_LIMITER\n        std::cout << std::endl;\n        std::cout << std::fixed << std::setprecision(16);\n        std::cout << \"t_l: (start) \" << t_l << std::endl;\n        std::cout << \"t_r: (start) \" << t_r << std::endl;\n#endif\n\n        for (unsigned int n = 0; n < parameters.newton_max_iterations(); ++n) {\n\n          const auto U_r = U + t_r * P;\n          const auto rho_r = view.density(U_r);\n          const auto rho_r_gamma = ryujin::pow(rho_r, gamma);\n          const auto rho_e_r = view.internal_energy(U_r);\n\n          auto psi_r =\n              relax_small * rho_r * rho_e_r - s_min * rho_r * rho_r_gamma;\n\n#ifndef DEBUG_EXPENSIVE_BOUNDS_CHECK\n          /*\n           * If psi_r > 0 the right state is fine, force returning t_r by\n           * setting t_l = t_r:\n           */\n          t_l = dealii::compare_and_apply_mask<\n              dealii::SIMDComparison::greater_than>(\n              psi_r, Number(0.), t_r, t_l);\n\n          /*\n           * If we have set t_l = t_r everywhere then all states state U_r\n           * with t_r obey the specific entropy inequality and we can\n           * break.\n           *\n           * This is a very important optimization: Only for 1 in (25 to\n           * 50) cases do we actually need to limit on the specific entropy\n           * because one of the right states failed. So we can skip\n           * constructing the left state U_l, which is expensive.\n           *\n           * This implies unfortunately that we might not accurately report\n           * whether the low_order update U itself obeyed bounds because\n           * U_r = U + t_r * P pushed us back into bounds. We thus skip\n           * this shortcut if `DEBUG_EXPENSIVE_BOUNDS_CHECK` is set.\n           */\n          if (t_l == t_r) {\n#ifdef DEBUG_OUTPUT_LIMITER\n            std::cout << \"shortcut: t_l == t_r\" << std::endl;\n            std::cout << \"psi_l:       \" << psi_l << std::endl;\n            std::cout << \"psi_r:       \" << psi_r << std::endl;\n            std::cout << \"t_l: (  \" << n << \"  ) \" << t_l << std::endl;\n            std::cout << \"t_r: (  \" << n << \"  ) \" << t_r << std::endl;\n#endif\n            break;\n          }\n#endif\n\n          const auto U_l = U + t_l * P;\n          const auto rho_l = view.density(U_l);\n          const auto rho_l_gamma = ryujin::pow(rho_l, gamma);\n          const auto rho_e_l = view.internal_energy(U_l);\n\n          auto psi_l =\n              relax_small * rho_l * rho_e_l - s_min * rho_l * rho_l_gamma;\n\n          /*\n           * Verify that the left state is within bounds. This property might\n           * be violated for relative CFL numbers larger than 1.\n           */\n          const auto lower_bound =\n              (ScalarNumber(1.) - relax) * s_min * rho_l * rho_l_gamma;\n          if (n == 0 &&\n              !(std::min(Number(0.), psi_l - lower_bound) == Number(0.))) {\n#ifdef DEBUG_OUTPUT\n            std::cout << std::fixed << std::setprecision(16);\n            std::cout\n                << \"Bounds violation: low-order specific entropy (critical)!\\n\";\n            std::cout << \"\\t\\tPsi left: 0 <= \" << psi_l << \"\\n\" << std::endl;\n#endif\n            success = false;\n          }\n\n#ifdef DEBUG_EXPENSIVE_BOUNDS_CHECK\n          /*\n           * If psi_r > 0 the right state is fine, force returning t_r by\n           * setting t_l = t_r:\n           */\n          t_l = dealii::compare_and_apply_mask<\n              dealii::SIMDComparison::greater_than>(\n              psi_r, Number(0.), t_r, t_l);\n#endif\n\n          /*\n           * Break if the window between t_l and t_r is within the prescribed\n           * tolerance:\n           */\n          const Number tolerance(parameters.newton_tolerance());\n          if (std::max(Number(0.), t_r - t_l - tolerance) == Number(0.)) {\n#ifdef DEBUG_OUTPUT_LIMITER\n            std::cout << \"break: t_l and t_r within tolerance\" << std::endl;\n            std::cout << \"psi_l:       \" << psi_l << std::endl;\n            std::cout << \"psi_r:       \" << psi_r << std::endl;\n            std::cout << \"t_l: (  \" << n << \"  ) \" << t_l << std::endl;\n            std::cout << \"t_r: (  \" << n << \"  ) \" << t_r << std::endl;\n#endif\n            break;\n          }\n\n          /* We got unlucky and have to perform a Newton step: */\n\n          const auto drho = view.density(P);\n          const auto drho_e_l = view.internal_energy_derivative(U_l) * P;\n          const auto drho_e_r = view.internal_energy_derivative(U_r) * P;\n          const auto dpsi_l =\n              rho_l * drho_e_l + (rho_e_l - gp1 * s_min * rho_l_gamma) * drho;\n          const auto dpsi_r =\n              rho_r * drho_e_r + (rho_e_r - gp1 * s_min * rho_r_gamma) * drho;\n\n          quadratic_newton_step(\n              t_l, t_r, psi_l, psi_r, dpsi_l, dpsi_r, Number(-1.));\n\n#ifdef DEBUG_OUTPUT_LIMITER\n          std::cout << \"psi_l:       \" << psi_l << std::endl;\n          std::cout << \"psi_r:       \" << psi_r << std::endl;\n          std::cout << \"dpsi_l:      \" << dpsi_l << std::endl;\n          std::cout << \"dpsi_r:      \" << dpsi_r << std::endl;\n          std::cout << \"t_l: (  \" << n << \"  ) \" << t_l << std::endl;\n          std::cout << \"t_r: (  \" << n << \"  ) \" << t_r << std::endl;\n#endif\n        }\n\n#ifdef DEBUG_EXPENSIVE_BOUNDS_CHECK\n        /*\n         * Verify that the new state is within bounds:\n         */\n        {\n          const auto U_new = U + t_l * P;\n          const auto rho_new = view.density(U_new);\n          const auto rho_new_gamma = ryujin::pow(rho_new, gamma);\n          const auto rho_e_new = view.internal_energy(U_new);\n\n          auto psi_new = relax_small * rho_new * rho_e_new -\n                         s_min * rho_new * rho_new_gamma;\n\n          const auto lower_bound =\n              (ScalarNumber(1.) - relax) * s_min * rho_new * rho_new_gamma;\n\n          const bool e_valid = std::min(Number(0.), rho_e_new) == Number(0.);\n          const bool psi_valid =\n              std::min(Number(0.), psi_new - lower_bound) == Number(0.);\n\n          if (!e_valid || !psi_valid) {\n#ifdef DEBUG_OUTPUT\n            std::cout << std::fixed << std::setprecision(16);\n            std::cout << \"Bounds violation: high-order specific entropy!\\n\";\n            std::cout << \"\\t\\trho e: 0 <= \" << rho_e_new << \"\\n\";\n            std::cout << \"\\t\\tPsi:   0 <= \" << psi_new << \"\\n\" << std::endl;\n#endif\n            success = false;\n          }\n        }\n#endif\n      }\n\n      return {t_l, success};\n    }\n\n  } // namespace Euler\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/riemann_solver.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2023 by the ryujin authors\n//\n\n#include \"riemann_solver.template.h\"\n\n#include <deal.II/base/vectorization.h>\n\nnamespace ryujin\n{\n  namespace Euler\n  {\n    /* instantiations */\n\n    template class RiemannSolver<1, NUMBER>;\n    template class RiemannSolver<2, NUMBER>;\n    template class RiemannSolver<3, NUMBER>;\n\n    template class RiemannSolver<1, dealii::VectorizedArray<NUMBER>>;\n    template class RiemannSolver<2, dealii::VectorizedArray<NUMBER>>;\n    template class RiemannSolver<3, dealii::VectorizedArray<NUMBER>>;\n\n  } // namespace Euler\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/riemann_solver.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"hyperbolic_system.h\"\n\n#include <simd.h>\n\n#include <deal.II/base/point.h>\n#include <deal.II/base/tensor.h>\n\nnamespace ryujin\n{\n  namespace Euler\n  {\n    template <typename ScalarNumber = double>\n    class RiemannSolverParameters : public dealii::ParameterAcceptor\n    {\n    public:\n      RiemannSolverParameters(const std::string &subsection = \"/RiemannSolver\")\n          : ParameterAcceptor(subsection)\n      {\n        if constexpr (std::is_same<ScalarNumber, double>::value)\n          newton_tolerance_ = 1.e-10;\n        else\n          newton_tolerance_ = 1.e-4;\n        add_parameter(\"newton tolerance\",\n                      newton_tolerance_,\n                      \"Tolerance for the quadratic newton stopping criterion\");\n\n        newton_max_iterations_ = 0;\n        add_parameter(\"newton max iterations\",\n                      newton_max_iterations_,\n                      \"Maximal number of quadratic newton iterations performed \"\n                      \"during limiting\");\n      }\n\n      ACCESSOR_READ_ONLY(newton_tolerance);\n      ACCESSOR_READ_ONLY(newton_max_iterations);\n\n    private:\n      ScalarNumber newton_tolerance_;\n      unsigned int newton_max_iterations_;\n    };\n\n\n    /**\n     * A fast approximative solver for the 1D Riemann problem. The solver\n     * ensures that the estimate \\f$\\lambda_{\\text{max}}\\f$ that is returned\n     * for the maximal wavespeed is a strict upper bound.\n     *\n     * The solver is based on @cite GuermondPopov2016b.\n     *\n     * @ingroup EulerEquations\n     */\n    template <int dim, typename Number = double>\n    class RiemannSolver\n    {\n    public:\n      /**\n       * @name Typedefs and constexpr constants\n       */\n      //@{\n\n      using View = HyperbolicSystemView<dim, Number>;\n\n      using ScalarNumber = typename View::ScalarNumber;\n\n      static constexpr auto problem_dimension = View::problem_dimension;\n\n      using state_type = typename View::state_type;\n\n      /**\n       * Number of components in a primitive state, we store \\f$[\\rho, v,\n       * p, a]\\f$, thus, 4.\n       */\n      static constexpr unsigned int riemann_data_size = 4;\n\n      /**\n       * The array type to store the expanded primitive state for the\n       * Riemann solver \\f$[\\rho, v, p, a]\\f$\n       */\n      using primitive_type = std::array<Number, riemann_data_size>;\n\n      using precomputed_type = typename View::precomputed_type;\n\n      using PrecomputedVector = typename View::PrecomputedVector;\n\n      using Parameters = RiemannSolverParameters<ScalarNumber>;\n\n      //@}\n      /**\n       * @name Compute wavespeed estimates\n       */\n      //@{\n\n      /**\n       * Constructor taking a HyperbolicSystem instance as argument\n       */\n      RiemannSolver(const HyperbolicSystem &hyperbolic_system,\n                    const Parameters &parameters,\n                    const PrecomputedVector &precomputed_values)\n          : hyperbolic_system(hyperbolic_system)\n          , parameters(parameters)\n          , precomputed_values(precomputed_values)\n      {\n      }\n\n      /**\n       * For two given 1D primitive states riemann_data_i and riemann_data_j,\n       * compute an estimation of an upper bound for the maximum wavespeed\n       * lambda.\n       */\n      Number compute(const primitive_type &riemann_data_i,\n                     const primitive_type &riemann_data_j) const;\n\n      /**\n       * For two given states U_i a U_j and a (normalized) \"direction\" n_ij\n       * compute an estimation of an upper bound for lambda.\n       *\n       * Returns a tuple consisting of lambda max and the number of Newton\n       * iterations used in the solver to find it.\n       */\n      Number compute(const state_type &U_i,\n                     const state_type &U_j,\n                     const unsigned int i,\n                     const unsigned int *js,\n                     const dealii::Tensor<1, dim, Number> &n_ij) const;\n\n      //@}\n\n    protected:\n      /** @name Internal functions used in the Riemann solver */\n      //@{\n\n      /**\n       * See @cite GuermondPopov2016b, page 912, (3.4).\n       *\n       * Cost: 1x pow, 1x division, 2x sqrt\n       */\n      Number f(const primitive_type &riemann_data, const Number p_star) const;\n\n\n      /**\n       * See @cite GuermondPopov2016b, page 912, (3.4).\n       *\n       * Cost: 1x pow, 3x division, 1x sqrt\n       */\n      Number df(const primitive_type &riemann_data, const Number &p_star) const;\n\n\n      /**\n       * See @cite GuermondPopov2016b, page 912, (3.3).\n       *\n       * Cost: 2x pow, 6x division, 2x sqrt\n       */\n      Number phi(const primitive_type &riemann_data_i,\n                 const primitive_type &riemann_data_j,\n                 const Number p_in) const;\n\n\n      /**\n       * See @cite GuermondPopov2016b, page 912, (3.3).\n       *\n       * Cost: 2x pow, 6x division, 2x sqrt\n       */\n      Number dphi(const primitive_type &riemann_data_i,\n                  const primitive_type &riemann_data_j,\n                  const Number &p) const;\n\n\n      /**\n       * See @cite GuermondPopov2016b, page 912, (3.3).\n       *\n       * The approximate Riemann solver is based on a function phi(p) that is\n       * montone increasing in p, concave down and whose (weak) third\n       * derivative is non-negative and locally bounded [1, p. 912]. Because\n       * we actually do not perform any iteration for computing our wavespeed\n       * estimate we can get away by only implementing a specialized variant\n       * of the phi function that computes phi(p_max). It inlines the\n       * implementation of the \"f\" function and eliminates all unnecessary\n       * branches in \"f\".\n       *\n       * Cost: 0x pow, 2x division, 2x sqrt\n       */\n      Number phi_of_p_max(const primitive_type &riemann_data_i,\n                          const primitive_type &riemann_data_j) const;\n\n\n      /**\n       * see @cite GuermondPopov2016b, page 912, (3.7)\n       *\n       * Cost: 0x pow, 1x division, 1x sqrt\n       */\n      Number lambda1_minus(const primitive_type &riemann_data,\n                           const Number p_star) const;\n\n\n      /**\n       * see @cite GuermondPopov2016b, page 912, (3.8)\n       *\n       * Cost: 0x pow, 1x division, 1x sqrt\n       */\n      Number lambda3_plus(const primitive_type &primitive_state,\n                          const Number p_star) const;\n\n\n      /**\n       * For two given primitive states <code>riemann_data_i</code> and\n       * <code>riemann_data_j</code>, and two guesses p_1 <= p* <= p_2,\n       * compute the gap in lambda between both guesses.\n       *\n       * See @cite GuermondPopov2016b, page 914, (4.4a), (4.4b), (4.5), and\n       * (4.6)\n       *\n       * Cost: 0x pow, 4x division, 4x sqrt\n       */\n      std::array<Number, 2> compute_gap(const primitive_type &riemann_data_i,\n                                        const primitive_type &riemann_data_j,\n                                        const Number p_1,\n                                        const Number p_2) const;\n\n\n      /**\n       * see @cite GuermondPopov2016b, page 912, (3.9)\n       *\n       * For two given primitive states <code>riemann_data_i</code> and\n       * <code>riemann_data_j</code>, and a guess p_2, compute an upper bound\n       * for lambda.\n       *\n       * Cost: 0x pow, 2x division, 2x sqrt (inclusive)\n       */\n      Number compute_lambda(const primitive_type &riemann_data_i,\n                            const primitive_type &riemann_data_j,\n                            const Number p_star) const;\n\n\n      /**\n       * Two-rarefaction approximation to p_star computed for two primitive\n       * states <code>riemann_data_i</code> and <code>riemann_data_j</code>.\n       *\n       * See @cite GuermondPopov2016b, page 914, (4.3)\n       *\n       * Cost: 2x pow, 2x division, 0x sqrt\n       */\n      Number p_star_two_rarefaction(const primitive_type &riemann_data_i,\n                                    const primitive_type &riemann_data_j) const;\n\n      /**\n       * Failsafe approximation to p_star computed for two primitive states\n       * <code>riemann_data_i</code> and <code>riemann_data_j</code>.\n       *\n       * See @cite ClaytonGuermondPopov-2022, (5.11):\n       *\n       * Cost: 0x pow, 3x division, 3x sqrt\n       */\n      Number p_star_failsafe(const primitive_type &riemann_data_i,\n                             const primitive_type &riemann_data_j) const;\n\n\n      /**\n       * For a given (2+dim dimensional) state vector <code>U</code>, and a\n       * (normalized) \"direction\" n_ij, first compute the corresponding\n       * projected state in the corresponding 1D Riemann problem, and then\n       * compute and return the Riemann data [rho, u, p, a] (used in the\n       * approximative Riemann solver).\n       */\n      primitive_type\n      riemann_data_from_state(const state_type &U,\n                              const dealii::Tensor<1, dim, Number> &n_ij) const;\n\n    private:\n      const HyperbolicSystem &hyperbolic_system;\n      const Parameters &parameters;\n      const PrecomputedVector &precomputed_values;\n      //@}\n    };\n  } // namespace Euler\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler/riemann_solver.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include \"riemann_solver.h\"\n\n#include <newton.h>\n#include <simd.h>\n\n// #define DEBUG_RIEMANN_SOLVER\n\nnamespace ryujin\n{\n  namespace Euler\n  {\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::f(const primitive_type &riemann_data,\n                                  const Number p_star) const\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n      const auto &gamma = view.gamma();\n\n      const auto &[rho, u, p, a] = riemann_data;\n\n      const Number Az = ScalarNumber(2.) / (rho * (gamma + Number(1.)));\n      const Number Bz =\n          (gamma - ScalarNumber(1.)) / (gamma + ScalarNumber(1.)) * p;\n      const Number radicand = Az / (p_star + Bz);\n      const Number true_value = (p_star - p) * std::sqrt(radicand);\n\n      const auto exponent =\n          ScalarNumber(0.5) * (gamma - ScalarNumber(1.)) / gamma;\n      const Number factor = ryujin::pow(p_star / p, exponent) - Number(1.);\n      const auto false_value =\n          ScalarNumber(2.) * a * factor / (gamma - ScalarNumber(1.));\n\n      return dealii::compare_and_apply_mask<\n          dealii::SIMDComparison::greater_than_or_equal>(\n          p_star, p, true_value, false_value);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::df(const primitive_type &riemann_data,\n                                   const Number &p_star) const\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      using ScalarNumber = typename get_value_type<Number>::type;\n      const auto &gamma = view.gamma();\n      const auto &gamma_inverse = view.gamma_inverse();\n      const auto &gamma_minus_one_inverse = view.gamma_minus_one_inverse();\n      const auto &gamma_plus_one_inverse = view.gamma_plus_one_inverse();\n\n      const auto &[rho, u, p, a] = riemann_data;\n\n      const Number radicand_inverse = ScalarNumber(0.5) * rho *\n                                      ((gamma + ScalarNumber(1.)) * p_star +\n                                       (gamma - ScalarNumber(1.)) * p);\n      const Number denominator =\n          (p_star + (gamma - ScalarNumber(1.)) * gamma_plus_one_inverse * p);\n      const Number true_value =\n          (denominator - ScalarNumber(0.5) * (p_star - p)) /\n          (denominator * std::sqrt(radicand_inverse));\n\n      const auto exponent =\n          (ScalarNumber(-1.) - gamma) * ScalarNumber(0.5) * gamma_inverse;\n      const Number factor = (gamma - ScalarNumber(1.)) * ScalarNumber(0.5) *\n                            gamma_inverse * ryujin::pow(p_star / p, exponent) /\n                            p;\n      const auto false_value =\n          factor * ScalarNumber(2.) * a * gamma_minus_one_inverse;\n\n      return dealii::compare_and_apply_mask<\n          dealii::SIMDComparison::greater_than_or_equal>(\n          p_star, p, true_value, false_value);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::phi(const primitive_type &riemann_data_i,\n                                    const primitive_type &riemann_data_j,\n                                    const Number p_in) const\n    {\n      const Number &u_i = riemann_data_i[1];\n      const Number &u_j = riemann_data_j[1];\n\n      return f(riemann_data_i, p_in) + f(riemann_data_j, p_in) + u_j - u_i;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::dphi(const primitive_type &riemann_data_i,\n                                     const primitive_type &riemann_data_j,\n                                     const Number &p) const\n    {\n      return df(riemann_data_i, p) + df(riemann_data_j, p);\n    }\n\n\n    /*\n     * The approximate Riemann solver is based on a function phi(p) that is\n     * montone increasing in p, concave down and whose (weak) third\n     * derivative is non-negative and locally bounded [1, p. 912]. Because we\n     * actually do not perform any iteration for computing our wavespeed\n     * estimate we can get away by only implementing a specialized variant of\n     * the phi function that computes phi(p_max). It inlines the\n     * implementation of the \"f\" function and eliminates all unnecessary\n     * branches in \"f\".\n     *\n     * Cost: 0x pow, 2x division, 2x sqrt\n     */\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::phi_of_p_max(\n        const primitive_type &riemann_data_i,\n        const primitive_type &riemann_data_j) const\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n      const auto &gamma = view.gamma();\n\n      const auto &[rho_i, u_i, p_i, a_i] = riemann_data_i;\n      const auto &[rho_j, u_j, p_j, a_j] = riemann_data_j;\n\n      const Number p_max = std::max(p_i, p_j);\n\n      const Number radicand_inverse_i = ScalarNumber(0.5) * rho_i *\n                                        ((gamma + ScalarNumber(1.)) * p_max +\n                                         (gamma - ScalarNumber(1.)) * p_i);\n\n      const Number value_i = (p_max - p_i) / std::sqrt(radicand_inverse_i);\n\n      const Number radicand_inverse_j = ScalarNumber(0.5) * rho_j *\n                                        ((gamma + ScalarNumber(1.)) * p_max +\n                                         (gamma - ScalarNumber(1.)) * p_j);\n\n      const Number value_j = (p_max - p_j) / std::sqrt(radicand_inverse_j);\n\n      return value_i + value_j + u_j - u_i;\n    }\n\n\n    /*\n     * Next we construct approximations for the two extreme wave speeds of\n     * the Riemann fan [1, p. 912, (3.7) + (3.8)] and compute an upper bound\n     * lambda_max of the maximal wave speed:\n     */\n\n\n    /*\n     * see [1], page 912, (3.7)\n     *\n     * Cost: 0x pow, 1x division, 1x sqrt\n     */\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::lambda1_minus(\n        const primitive_type &riemann_data, const Number p_star) const\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n      const auto &gamma = view.gamma();\n      const auto &gamma_inverse = view.gamma_inverse();\n      const auto factor =\n          (gamma + ScalarNumber(1.0)) * ScalarNumber(0.5) * gamma_inverse;\n\n      const auto &[rho, u, p, a] = riemann_data;\n      const auto inv_p = ScalarNumber(1.0) / p;\n\n      const Number tmp = positive_part((p_star - p) * inv_p);\n\n      return u - a * std::sqrt(ScalarNumber(1.0) + factor * tmp);\n    }\n\n\n    /*\n     * see [1], page 912, (3.8)\n     *\n     * Cost: 0x pow, 1x division, 1x sqrt\n     */\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::lambda3_plus(\n        const primitive_type &primitive_state, const Number p_star) const\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n      const auto &gamma = view.gamma();\n      const auto &gamma_inverse = view.gamma_inverse();\n      const Number factor =\n          (gamma + ScalarNumber(1.0)) * ScalarNumber(0.5) * gamma_inverse;\n\n      const auto &[rho, u, p, a] = primitive_state;\n      const auto inv_p = ScalarNumber(1.0) / p;\n\n      const Number tmp = positive_part((p_star - p) * inv_p);\n      return u + a * std::sqrt(Number(1.0) + factor * tmp);\n    }\n\n\n    /**\n     * For two given primitive states <code>riemann_data_i</code> and\n     * <code>riemann_data_j</code>, and two guesses p_1 <= p* <= p_2,\n     * compute the gap in lambda between both guesses.\n     *\n     * See [1], page 914, (4.4a), (4.4b), (4.5), and (4.6)\n     *\n     * Cost: 0x pow, 4x division, 4x sqrt\n     */\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline std::array<Number, 2>\n    RiemannSolver<dim, Number>::compute_gap(\n        const std::array<Number, 4> &riemann_data_i,\n        const std::array<Number, 4> &riemann_data_j,\n        const Number p_1,\n        const Number p_2) const\n    {\n      const Number nu_11 = lambda1_minus(riemann_data_i, p_2 /*SIC!*/);\n      const Number nu_12 = lambda1_minus(riemann_data_i, p_1 /*SIC!*/);\n\n      const Number nu_31 = lambda3_plus(riemann_data_j, p_1);\n      const Number nu_32 = lambda3_plus(riemann_data_j, p_2);\n\n      const Number lambda_max =\n          std::max(positive_part(nu_32), negative_part(nu_11));\n\n      const Number gap =\n          std::max(std::abs(nu_32 - nu_31), std::abs(nu_12 - nu_11));\n\n      return {{gap, lambda_max}};\n    }\n\n\n    /*\n     * For two given primitive states <code>riemann_data_i</code> and\n     * <code>riemann_data_j</code>, and a guess p_2, compute an upper bound\n     * for lambda.\n     *\n     * This is the same lambda_max as computed by compute_gap. The function\n     * simply avoids a number of unnecessary computations (in case we do\n     * not need to know the gap).\n     *\n     * Cost: 0x pow, 2x division, 2x sqrt\n     */\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::compute_lambda(\n        const primitive_type &riemann_data_i,\n        const primitive_type &riemann_data_j,\n        const Number p_star) const\n    {\n      const Number nu_11 = lambda1_minus(riemann_data_i, p_star);\n      const Number nu_32 = lambda3_plus(riemann_data_j, p_star);\n\n      return std::max(positive_part(nu_32), negative_part(nu_11));\n    }\n\n\n    /*\n     * Two-rarefaction approximation to p_star computed for two primitive\n     * states <code>riemann_data_i</code> and <code>riemann_data_j</code>.\n     *\n     * See [1], page 914, (4.3)\n     *\n     * Cost: 2x pow, 2x division, 0x sqrt\n     */\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::p_star_two_rarefaction(\n        const primitive_type &riemann_data_i,\n        const primitive_type &riemann_data_j) const\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n      const auto &gamma = view.gamma();\n      const auto &gamma_inverse = view.gamma_inverse();\n      const auto &gamma_minus_one_inverse = view.gamma_minus_one_inverse();\n\n      const auto &[rho_i, u_i, p_i, a_i] = riemann_data_i;\n      const auto &[rho_j, u_j, p_j, a_j] = riemann_data_j;\n      const auto inv_p_j = ScalarNumber(1.) / p_j;\n\n      /*\n       * Nota bene (cf. [1, (4.3)]):\n       *   a_Z^0 * sqrt(1 - b * rho_Z) = a_Z * (1 - b * rho_Z)\n       * We have computed a_Z already, so we are simply going to use this\n       * identity below:\n       */\n\n      const auto factor = (gamma - ScalarNumber(1.)) * ScalarNumber(0.5);\n\n      /*\n       * Nota bene (cf. [1, (3.6)]: The condition \"numerator > 0\" is the\n       * well-known non-vacuum condition. In case we encounter numerator <= 0\n       * then p_star = 0 is the correct pressure to compute the wave speed.\n       * Therefore, all we have to do is to take the positive part of the\n       * expression:\n       */\n\n      const Number numerator = positive_part(a_i + a_j - factor * (u_j - u_i));\n      const Number denominator =\n          a_i * ryujin::pow(p_i * inv_p_j, -factor * gamma_inverse) + a_j;\n\n      const auto exponent = ScalarNumber(2.0) * gamma * gamma_minus_one_inverse;\n\n      const auto p_1_tilde =\n          p_j * ryujin::pow(numerator / denominator, exponent);\n\n#ifdef DEBUG_RIEMANN_SOLVER\n      std::cout << \"p_star_two_rarefaction = \" << p_1_tilde << std::endl;\n#endif\n      return p_1_tilde;\n    }\n\n\n    /*\n     * Failsafe approximation to p_star computed for two primitive\n     * states <code>riemann_data_i</code> and <code>riemann_data_j</code>.\n     *\n     * See [1], page 914, (4.3)\n     *\n     * Cost: 2x pow, 2x division, 0x sqrt\n     */\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::p_star_failsafe(\n        const primitive_type &riemann_data_i,\n        const primitive_type &riemann_data_j) const\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n      const auto &gamma = view.gamma();\n\n      const auto &[rho_i, u_i, p_i, a_i] = riemann_data_i;\n      const auto &[rho_j, u_j, p_j, a_j] = riemann_data_j;\n\n      /*\n       * Compute (5.11) formula for \\tilde p_2^\\ast:\n       *\n       * Cost: 0x pow, 3x division, 3x sqrt\n       */\n\n      const Number p_max = std::max(p_i, p_j);\n\n      Number radicand_i = ScalarNumber(2.) * p_max;\n      radicand_i /=\n          rho_i * ((gamma + Number(1.)) * p_max + (gamma - Number(1.)) * p_i);\n\n      const Number x_i = std::sqrt(radicand_i);\n\n      Number radicand_j = ScalarNumber(2.) * p_max;\n      radicand_j /=\n          rho_j * ((gamma + Number(1.)) * p_max + (gamma - Number(1.)) * p_j);\n\n      const Number x_j = std::sqrt(radicand_j);\n\n      const Number a = x_i + x_j;\n      const Number b = u_j - u_i;\n      const Number c = -p_i * x_i - p_j * x_j;\n\n      const Number base = (-b + std::sqrt(b * b - ScalarNumber(4.) * a * c)) /\n                          (ScalarNumber(2.) * a);\n      const Number p_2_tilde = base * base;\n\n#ifdef DEBUG_RIEMANN_SOLVER\n      std::cout << \"p_star_failsafe = \" << p_2_tilde << std::endl;\n#endif\n      return p_2_tilde;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    RiemannSolver<dim, Number>::riemann_data_from_state(\n        const state_type &U, const dealii::Tensor<1, dim, Number> &n_ij) const\n        -> primitive_type\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      const auto rho = view.density(U);\n      const auto rho_inverse = Number(1.0) / rho;\n\n      const auto m = view.momentum(U);\n      const auto proj_m = n_ij * m;\n      const auto perp = m - proj_m * n_ij;\n\n      const auto E =\n          view.total_energy(U) - Number(0.5) * perp.norm_square() * rho_inverse;\n\n      using state_type_1d =\n          typename HyperbolicSystemView<1, Number>::state_type;\n      const auto view_1d = hyperbolic_system.view<1, Number>();\n\n      const auto state = state_type_1d{{rho, proj_m, E}};\n      const auto p = view_1d.pressure(state);\n      const auto a = view_1d.speed_of_sound(state);\n      return {{rho, proj_m * rho_inverse, p, a}};\n    }\n\n\n    template <int dim, typename Number>\n    Number RiemannSolver<dim, Number>::compute(\n        const primitive_type &riemann_data_i,\n        const primitive_type &riemann_data_j) const\n    {\n      /*\n       * For exactly solving the Riemann problem we need to start with a\n       * good upper and lower bound, p_1 <= p_star <= p_2, for finding\n       * phi(p_star) == 0. This implies that we have to ensure that\n       * phi(p_2) >= 0 and phi(p_1) <= 0.\n       *\n       * Instead of solving the Riemann problem exactly, however we will\n       * simply use the upper bound p_2 (with p_2 >= p_star) to compute\n       * lambda_max and return the estimate.\n       *\n       * We will use three candidates, p_min, p_max and the two rarefaction\n       * approximation p_star_tilde. We have (up to round-off errors) that\n       * phi(p_star_tilde) >= 0. So this is a safe upper bound, it might\n       * just be too large.\n       *\n       * Depending on the sign of phi(p_max) we select the following ranges:\n       *\n       *   phi(p_max) <  0:\n       *     p_1  <-  p_max   and   p_2  <-  p_star_tilde\n       *\n       *   phi(p_max) >= 0:\n       *     p_1  <-  p_min   and   p_2  <-  min(p_max, p_star_tilde)\n       *\n       * Nota bene:\n       *\n       *  - The special case phi(p_max) == 0 as discussed in [1] is already\n       *    contained in the second condition.\n       *\n       *  - In principle, we would have to treat the case phi(p_min) > 0 as\n       *    well. This corresponds to two expansion waves and a good\n       *    estimate for the wavespeed is obtained by simply computing\n       *    lambda_max with p_2 = 0.\n       *\n       *    However, it turns out that numerically in this case we will\n       *    have\n       *\n       *      0 < p_star <= p_star_tilde <= p_min <= p_max.\n       *\n       *    So it is sufficient to end up with p_2 = p_star_tilde (!!) to\n       *    compute the exact same wave speed as for p_2 = 0.\n       *\n       *    Note: If for some reason p_star should be computed exactly,\n       *    then p_1 has to be set to zero. This can be done efficiently by\n       *    simply checking for p_2 < p_1 and setting p_1 <- 0 if\n       *    necessary.\n       */\n\n      const auto &[rho_i, u_i, p_i, a_i] = riemann_data_i;\n      const auto &[rho_j, u_j, p_j, a_j] = riemann_data_j;\n\n#ifdef DEBUG_RIEMANN_SOLVER\n      std::cout << \"rho_left: \" << rho_i << std::endl;\n      std::cout << \"u_left: \" << u_i << std::endl;\n      std::cout << \"p_left: \" << p_i << std::endl;\n      std::cout << \"a_left: \" << a_i << std::endl;\n      std::cout << \"rho_right: \" << rho_j << std::endl;\n      std::cout << \"u_right: \" << u_j << std::endl;\n      std::cout << \"p_right: \" << p_j << std::endl;\n      std::cout << \"a_right: \" << a_j << std::endl;\n#endif\n\n      const Number p_max = std::max(p_i, p_j);\n\n      const Number rarefaction =\n          p_star_two_rarefaction(riemann_data_i, riemann_data_j);\n      const Number failsafe = p_star_failsafe(riemann_data_i, riemann_data_j);\n      const Number p_star_tilde = std::min(rarefaction, failsafe);\n\n      const Number phi_p_max = phi_of_p_max(riemann_data_i, riemann_data_j);\n\n      Number p_2 =\n          dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(\n              phi_p_max,\n              Number(0.),\n              p_star_tilde,\n              std::min(p_max, p_star_tilde));\n\n#ifdef DEBUG_RIEMANN_SOLVER\n      std::cout << \"   p^*_tilde  = \" << p_2 << \"\\n\";\n      std::cout << \"   phi(p_*_t) = \"\n                << phi(riemann_data_i, riemann_data_j, p_2) << std::endl;\n#endif\n\n      /*\n       * If we do no Newton iteration, cut it short:\n       */\n\n      if (parameters.newton_max_iterations() == 0) {\n        const auto lambda_max =\n            compute_lambda(riemann_data_i, riemann_data_j, p_2);\n\n#ifdef DEBUG_RIEMANN_SOLVER\n        std::cout << \"-> lambda_max = \" << lambda_max << std::endl;\n#endif\n        return lambda_max;\n      }\n\n      /*\n       * Compute p_1 and ensure that p_1 < p_2. If we hit a case with two\n       * expansions we might indeed have that p_star_tilde < p_1. Set p_1 =\n       * p_2 in this case.\n       */\n\n      const Number p_min = std::min(riemann_data_i[2], riemann_data_j[2]);\n\n      Number p_1 =\n          dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(\n              phi_p_max, Number(0.), p_max, p_min);\n\n      p_1 = dealii::compare_and_apply_mask<\n          dealii::SIMDComparison::less_than_or_equal>(p_1, p_2, p_1, p_2);\n\n      /*\n       * Step 2: Perform quadratic Newton iteration.\n       *\n       * See [1], p. 915f (4.8) and (4.9)\n       */\n\n      auto [gap, lambda_max] =\n          compute_gap(riemann_data_i, riemann_data_j, p_1, p_2);\n\n#ifdef DEBUG_RIEMANN_SOLVER\n      std::cout << std::fixed << std::setprecision(16);\n      std::cout << \"p_1: (start) \" << p_1 << std::endl;\n      std::cout << \"p_2: (start) \" << p_2 << std::endl;\n      std::cout << \"gap: (start) \" << gap << std::endl;\n      std::cout << \"l_m: (start) \" << lambda_max << std::endl;\n#endif\n\n      for (unsigned int i = 0; i < parameters.newton_max_iterations(); ++i) {\n\n        /* We accept our current guess if we reach the tolerance... */\n        const Number tolerance(parameters.newton_tolerance());\n        if (std::max(Number(0.), gap - tolerance) == Number(0.)) {\n#ifdef DEBUG_RIEMANN_SOLVER\n          std::cout << \"converged after \" << i << \" iterations.\" << std::endl;\n#endif\n          break;\n        }\n\n        // FIXME: Fuse these computations:\n        const Number phi_p_1 = phi(riemann_data_i, riemann_data_j, p_1);\n        const Number phi_p_2 = phi(riemann_data_i, riemann_data_j, p_2);\n        const Number dphi_p_1 = dphi(riemann_data_i, riemann_data_j, p_1);\n        const Number dphi_p_2 = dphi(riemann_data_i, riemann_data_j, p_2);\n\n        quadratic_newton_step(p_1, p_2, phi_p_1, phi_p_2, dphi_p_1, dphi_p_2);\n\n        /* Update  lambda_max and gap: */\n        auto [gap_new, lambda_max_new] =\n            compute_gap(riemann_data_i, riemann_data_j, p_1, p_2);\n        gap = gap_new;\n        lambda_max = lambda_max_new;\n\n#ifdef DEBUG_RIEMANN_SOLVER\n        std::cout << \"phi_p_1:     \" << phi_p_1 << std::endl;\n        std::cout << \"phi_p_2:     \" << phi_p_2 << std::endl;\n        std::cout << \"dphi_p_1:    \" << dphi_p_1 << std::endl;\n        std::cout << \"dphi_p_2:    \" << dphi_p_2 << std::endl;\n        std::cout << \"p_1: (  \" << i << \"  ) \" << p_1 << std::endl;\n        std::cout << \"p_2: (  \" << i << \"  ) \" << p_2 << std::endl;\n        std::cout << \"gap:         \" << gap << std::endl;\n        std::cout << \"l_m:         \" << lambda_max << std::endl;\n#endif\n      }\n\n#ifdef DEBUG_RIEMANN_SOLVER\n      std::cout << \"-> lambda_max = \" << lambda_max << std::endl;\n#endif\n\n      return lambda_max;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number RiemannSolver<dim, Number>::compute(\n        const state_type &U_i,\n        const state_type &U_j,\n        const unsigned int /*i*/,\n        const unsigned int * /*js*/,\n        const dealii::Tensor<1, dim, Number> &n_ij) const\n    {\n      const auto riemann_data_i = riemann_data_from_state(U_i, n_ij);\n      const auto riemann_data_j = riemann_data_from_state(U_j, n_ij);\n\n      return compute(riemann_data_i, riemann_data_j);\n    }\n\n  } // namespace Euler\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_aeos/CMakeLists.txt",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2023 - 2024 by the ryujin authors\n##\n\nadd_library(obj_euler_aeos OBJECT\n  equation_dispatch.cc\n  equation_of_state_library.cc\n  initial_state_library.cc\n  limiter.cc\n  riemann_solver.cc\n  )\nset_target_properties(obj_euler_aeos PROPERTIES LINKER_LANGUAGE CXX)\ndeal_ii_setup_target(obj_euler_aeos)\ntarget_link_libraries(obj_euler_aeos obj_common ${EXTERNAL_TARGETS})\n# Propagate the current source directory with PUBLIC visibility\ntarget_include_directories(obj_euler_aeos PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})\n"
  },
  {
    "path": "source/euler_aeos/Makefile",
    "content": "default: all\n.PHONY: default\n\n%:\n\t@cd .. && make $@\n.PHONY: %\n"
  },
  {
    "path": "source/euler_aeos/description.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"../stub_parabolic_module.h\"\n#include \"../stub_parabolic_system.h\"\n#include \"hyperbolic_system.h\"\n#include \"indicator.h\"\n#include \"limiter.h\"\n#include \"riemann_solver.h\"\n\nnamespace ryujin\n{\n  namespace EulerAEOS\n  {\n    /**\n     * A struct that contains all equation specific classes describing the\n     * chosen hyperbolic system, the indicator, the limiter and\n     * (approximate) Riemann solver.\n     *\n     * The compressible Euler equations of gas dynamics. Generalized\n     * implementation with a modified approximative Riemann solver,\n     * indicator, and limiter suitable for arbitrary equations of state.\n     *\n     * The parabolic subsystem is chosen to be the identity.\n     *\n     * @ingroup EulerEquations\n     */\n    struct Description {\n      using HyperbolicSystem = EulerAEOS::HyperbolicSystem;\n\n      template <int dim, typename Number = double>\n      using HyperbolicSystemView = EulerAEOS::HyperbolicSystemView<dim, Number>;\n\n      using ParabolicSystem = ryujin::StubParabolicSystem;\n\n      template <int dim, typename Number = double>\n      using ParabolicModule =\n          ryujin::StubParabolicModule<Description, dim, Number>;\n\n      template <int dim, typename Number = double>\n      using Indicator = EulerAEOS::Indicator<dim, Number>;\n\n      template <int dim, typename Number = double>\n      using Limiter = EulerAEOS::Limiter<dim, Number>;\n\n      template <int dim, typename Number = double>\n      using RiemannSolver = EulerAEOS::RiemannSolver<dim, Number>;\n    };\n  } // namespace EulerAEOS\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_aeos/equation_dispatch.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2024 by the ryujin authors\n//\n\n#include \"description.h\"\n\n#include <compile_time_options.h>\n#include <equation_dispatch.h>\n\nnamespace ryujin\n{\n  namespace EulerAEOS\n  {\n    Dispatch<Description, NUMBER> dispatch_instance(\"euler aeos\");\n  } // namespace EulerAEOS\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_aeos/equation_of_state.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2024 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"convenience_macros.h\"\n\n#include <deal.II/base/exceptions.h>\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/tensor.h>\n\n#include <string>\n\nnamespace ryujin\n{\n  namespace EquationOfStateLibrary\n  {\n    /**\n     * A small abstract base class to group configuration options for an\n     * equation of state.\n     *\n     * @ingroup EulerEquations\n     */\n    class EquationOfState : public dealii::ParameterAcceptor\n    {\n    public:\n      /**\n       * Constructor taking EOS name @p name and a subsection @p subsection\n       * as an argument. The dealii::ParameterAcceptor is initialized with\n       * the subsubsection `subsection + \"/\" + name`.\n       */\n      EquationOfState(const std::string &name, const std::string &subsection)\n          : ParameterAcceptor(subsection + \"/\" + name)\n          , name_(name)\n      {\n        /*\n         * If necessary derived EOS can override the covolume b that is\n         * used in the interpolatory NASG eos.\n         */\n        interpolation_b_ = 0.;\n\n        /*\n         * If necessary derived EOS can override the reference pressure\n         * that is used in the interpolatory NASG eos.\n         */\n        interpolation_pinfty_ = 0.;\n\n        /*\n         * If necessary derived EOS can override the reference specific\n         * internal energy q that is used in the interpolatory NASG eos.\n         */\n        interpolation_q_ = 0.;\n      }\n\n      /**\n       * Return the pressure given density @p rho and specific internal\n       * energy @p e.\n       */\n      virtual double pressure(double rho, double e) const = 0;\n\n      /**\n       * Return the specific internal energy @p e for a given density @p\n       * rho and pressure @p p.\n       */\n      virtual double specific_internal_energy(double rho, double p) const = 0;\n\n      /**\n       * Return the temperature @p T for a given density @p\n       * rho and specific internal energy @p e.\n       */\n      virtual double temperature(double rho, double e) const = 0;\n\n      /**\n       * Return the sound speed @p c for a given density @p rho and\n       * specific internal energy  @p e.\n       */\n      virtual double speed_of_sound(double rho, double e) const = 0;\n\n      /**\n       * Return the interpolation covolume constant (b).\n       */\n      ACCESSOR_READ_ONLY(interpolation_b)\n\n      /**\n       * Return the interpolation reference pressure (pinfty).\n       */\n      ACCESSOR_READ_ONLY(interpolation_pinfty)\n\n      /**\n       * Return the interpolation reference specific internal energy (q).\n       */\n      ACCESSOR_READ_ONLY(interpolation_q)\n\n      /**\n       * Return the name of the EOS as (const reference) std::string\n       */\n      ACCESSOR_READ_ONLY(name)\n\n    protected:\n      double interpolation_b_;\n      double interpolation_pinfty_;\n      double interpolation_q_;\n\n    private:\n      const std::string name_;\n    };\n\n  } // namespace EquationOfStateLibrary\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/euler_aeos/equation_of_state_function.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"equation_of_state.h\"\n\n#include <deal.II/base/function_parser.h>\n\nnamespace ryujin\n{\n  namespace EquationOfStateLibrary\n  {\n    /**\n     * A user-specified equation of state\n     *\n     * @ingroup EulerEquations\n     */\n    class Function : public EquationOfState\n    {\n    public:\n      using EquationOfState::pressure;\n      using EquationOfState::specific_internal_energy;\n      using EquationOfState::speed_of_sound;\n      using EquationOfState::temperature;\n\n      Function(const std::string &subsection)\n          : EquationOfState(\"function\", subsection)\n      {\n        p_expression_ = \"(1.4 - 1.0) * rho * e\";\n        add_parameter(\n            \"pressure\",\n            p_expression_,\n            \"A function expression for the pressure as a function of density, \"\n            \"rho, and specific internal energy, e: p(rho, e)\");\n\n        sie_expression_ = \"p / (rho * (1.4 - 1.0))\";\n        add_parameter(\n            \"specific internal energy\",\n            sie_expression_,\n            \"A function expression for the specific internal energy as a \"\n            \"function of density, rho, and pressure, p: e(rho, p)\");\n\n        temperature_expression_ = \"e / 718.\";\n        add_parameter(\"temperature\",\n                      temperature_expression_,\n                      \"A function expression for the temperature as a \"\n                      \"function of density, rho, and specific internal energy, \"\n                      \"e: T(rho, e)\");\n\n        sos_expression_ = \"sqrt(1.4 * (1.4 - 1.0) * e)\";\n        add_parameter(\n            \"speed of sound\",\n            sos_expression_,\n            \"A function expression for the speed of sound as a function of \"\n            \"density, rho, and specific internal energy, e: s(rho, e)\");\n\n        add_parameter(\n            \"interpolatory covolume b\",\n            this->interpolation_b_,\n            \"The interpolatory maximum compressibility constant b used when \"\n            \"constructing the interpolatory equation of state\");\n\n        add_parameter(\"interpolatory reference pressure\",\n                      this->interpolation_pinfty_,\n                      \"The interpolatory reference pressure p_infty used when \"\n                      \"constructing the interpolatory equation of state\");\n\n        add_parameter(\n            \"interpolatory reference specific internal energy\",\n            this->interpolation_q_,\n            \"The interpolatory reference specific internal energy q used when \"\n            \"constructing the interpolatory equation of state\");\n\n        /*\n         * Set up the muparser object with the final equation of state\n         * description from the parameter file:\n         */\n        const auto set_up_muparser = [this] {\n          p_function_ = std::make_unique<dealii::FunctionParser<2>>();\n          p_function_->initialize(\"rho,e\", p_expression_, {});\n\n          sie_function_ = std::make_unique<dealii::FunctionParser<2>>();\n          sie_function_->initialize(\"rho,p\", sie_expression_, {});\n\n          temperature_function_ = std::make_unique<dealii::FunctionParser<2>>();\n          temperature_function_->initialize(\n              \"rho,e\", temperature_expression_, {});\n\n          sos_function_ = std::make_unique<dealii::FunctionParser<2>>();\n          sos_function_->initialize(\"rho,e\", sos_expression_, {});\n        };\n\n        set_up_muparser();\n        ParameterAcceptor::parse_parameters_call_back.connect(set_up_muparser);\n      }\n\n      double pressure(double rho, double e) const final\n      {\n        return p_function_->value(dealii::Point<2>(rho, e));\n      }\n\n      double specific_internal_energy(double rho, double p) const final\n      {\n        return sie_function_->value(dealii::Point<2>(rho, p));\n      }\n\n      double temperature(double rho, double e) const final\n      {\n        return temperature_function_->value(dealii::Point<2>(rho, e));\n      }\n\n      double speed_of_sound(double rho, double e) const final\n      {\n        return sos_function_->value(dealii::Point<2>(rho, e));\n      }\n\n    private:\n      std::string p_expression_;\n      std::string sie_expression_;\n      std::string sos_expression_;\n      std::string temperature_expression_;\n\n      std::unique_ptr<dealii::FunctionParser<2>> p_function_;\n      std::unique_ptr<dealii::FunctionParser<2>> sie_function_;\n      std::unique_ptr<dealii::FunctionParser<2>> sos_function_;\n      std::unique_ptr<dealii::FunctionParser<2>> temperature_function_;\n    };\n  } // namespace EquationOfStateLibrary\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_aeos/equation_of_state_jones_wilkins_lee.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"equation_of_state.h\"\n\nnamespace ryujin\n{\n  namespace EquationOfStateLibrary\n  {\n    /**\n     * The Jones-Wilkins-Lee equation of state. See (16a) in:\n     * \"JWL Equation of State\" by Ralph Menikoff (LA-UR-15-29536). We are\n     * assuming that the reference temperature T_r is chosen such that E_r = 0.\n     * Default parameters taken from Table 1 in reference.\n     *\n     * @ingroup EulerEquations\n     */\n    class JonesWilkinsLee : public EquationOfState\n    {\n    public:\n      using EquationOfState::pressure;\n      using EquationOfState::specific_internal_energy;\n      using EquationOfState::speed_of_sound;\n      using EquationOfState::temperature;\n\n      JonesWilkinsLee(const std::string &subsection)\n          : EquationOfState(\"jones wilkins lee\", subsection)\n      {\n        capA = 6.3207e13; // [Pa]\n        this->add_parameter(\"A\", capA, \"The A constant\");\n\n        capB = -4.472e9; // [Pa]\n        this->add_parameter(\"B\", capB, \"The B constant\");\n\n        R1 = 11.3; // [unitless]\n        this->add_parameter(\"R1\", R1, \"The R1 constant\");\n\n        R2 = 1.13; // [unitless]\n        this->add_parameter(\"R2\", R2, \"The R2 constant\");\n\n        omega = 0.8938; // [unitless]\n        this->add_parameter(\"omega\", omega, \"The Gruneisen coefficient\");\n\n        rho_0 = 1895; // [Kg / m^3]\n        this->add_parameter(\"rho_0\", rho_0, \"The reference density\");\n\n        q_0 = 0.0; // [J / Kg]\n        this->add_parameter(\"q_0\", q_0, \"The specific internal energy offset\");\n\n        cv_ = 2487. / rho_0; // [J / (Kg * K)]\n        this->add_parameter(\n            \"c_v\", cv_, \"The specific heat capacity at constant volume\");\n      }\n\n      /**\n       * The pressure is given by\n       * \\f{align}\n       *   p = A(1 - \\omega / R_1 \\rho / \\rho_0) e^{(-R_1 \\rho_0 / \\rho)}\n       *     + B(1 - \\omega / R_2 \\rho/ \\rho_0) e^{(-R_2 \\rho_0 / \\rho)}\n       *     + \\omega \\rho (e + q_0)\n       * \\f}\n       */\n      double pressure(double rho, double e) const final\n      {\n\n        const auto ratio = rho / rho_0;\n\n        const auto first_term =\n            capA * (1. - omega / R1 * ratio) * std::exp(-R1 * 1. / ratio);\n        const auto second_term =\n            capB * (1. - omega / R2 * ratio) * std::exp(-R2 * 1. / ratio);\n\n        return first_term + second_term + omega * rho * (e + q_0);\n      }\n\n      /**\n       * The specific internal energy is given by\n       * \\f{align}\n       *   \\omega \\rho e = p\n       *   - A(1 - \\omega / R_1 \\rho / \\rho_0) e^{(-R_1 \\rho_0 / \\rho)}\n       *   - B(1 - \\omega / R_2 \\rho/ \\rho_0) e^{(-R_2 \\rho_0 / \\rho)}\n       * \\f}\n       */\n      double specific_internal_energy(double rho, double p) const final\n      {\n        const auto ratio = rho / rho_0;\n\n        const auto first_term =\n            capA * (1. - omega / R1 * ratio) * std::exp(-R1 * 1. / ratio);\n        const auto second_term =\n            capB * (1. - omega / R2 * ratio) * std::exp(-R2 * 1. / ratio);\n\n        return (p - first_term - second_term) / (rho * omega);\n      }\n\n      /**\n       * The temperature is given by\n       * \\f{align}\n       *   c_v T = e + q_0 - 1 / \\rho_0 * (A / R_1 * e^{(-R_1 \\rho_0 / \\rho)}\n       *         + B / R_2 * e^{(-R_2 \\rho_0 / \\rho)})\n       * \\f}\n       */\n      double temperature(double rho, double e) const final\n      {\n        /* Using (16a) of LA-UR-15-29536 */\n        const auto ratio = rho / rho_0;\n\n        const auto first_term = capA / R1 * std::exp(-R1 * 1. / ratio);\n        const auto second_term = capB / R2 * std::exp(-R2 * 1. / ratio);\n\n        return (e + q_0 - 1. / rho_0 * (first_term + second_term)) / cv_;\n      }\n\n      /**\n       * The speed of sound is given by\n       */\n      double speed_of_sound(double rho, double e) const final\n      {\n        /* FIXME: Need to cross reference with literature */\n\n        const auto t1 = omega * rho / (R1 * rho_0);\n        const auto factor1 = omega * (1. - t1) * (1. + 1. / t1) - t1;\n        const auto first_term =\n            capA / rho * factor1 * std::exp(-1. / t1 / omega);\n\n        const auto t2 = omega * rho / (R2 * rho_0);\n        const auto factor2 = omega * (1. - t2) * (1. + 1. / t2) - t2;\n        const auto second_term =\n            capB / rho * factor2 * std::exp(-1. / t2 / omega);\n\n        const auto third_term = omega * (omega + 1.) * e;\n\n        return std::sqrt(first_term + second_term + third_term);\n      }\n\n    private:\n      double capA;\n      double capB;\n      double R1;\n      double R2;\n      double omega;\n      double rho_0;\n      double q_0;\n      double cv_;\n    };\n  } // namespace EquationOfStateLibrary\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_aeos/equation_of_state_library.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#include \"equation_of_state_library.h\"\n\n#include \"equation_of_state_function.h\"\n#include \"equation_of_state_jones_wilkins_lee.h\"\n#include \"equation_of_state_noble_abel_stiffened_gas.h\"\n#include \"equation_of_state_polytropic_gas.h\"\n#include \"equation_of_state_pressureless.h\"\n#include \"equation_of_state_sesame.h\"\n#include \"equation_of_state_simple_macaw.h\"\n#include \"equation_of_state_van_der_waals.h\"\n\nnamespace ryujin\n{\n  namespace EquationOfStateLibrary\n  {\n    /**\n     * Populate a given container with all equation of states defined in\n     * this namespace.\n     *\n     * @ingroup EulerEquations\n     */\n\n    void populate_equation_of_state_list(\n        equation_of_state_list_type &equation_of_state_list,\n        const std::string &subsection)\n    {\n      auto add = [&](auto &&object) {\n        equation_of_state_list.emplace(std::move(object));\n      };\n\n      add(std::make_shared<Function>(subsection));\n      add(std::make_shared<JonesWilkinsLee>(subsection));\n      add(std::make_shared<NobleAbelStiffenedGas>(subsection));\n      add(std::make_shared<PolytropicGas>(subsection));\n      add(std::make_shared<Sesame>(subsection));\n      add(std::make_shared<SimpleMacaw>(subsection));\n      add(std::make_shared<VanDerWaals>(subsection));\n      add(std::make_shared<Pressureless>(subsection));\n    }\n  } // namespace EquationOfStateLibrary\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_aeos/equation_of_state_library.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"equation_of_state.h\"\n\nnamespace ryujin\n{\n  namespace EquationOfStateLibrary\n  {\n    using equation_of_state_list_type =\n        std::set<std::shared_ptr<EquationOfState>>;\n\n    /**\n     * Populate a given container with all equation of states defined in\n     * this namespace.\n     *\n     * @ingroup EulerEquations\n     */\n    void populate_equation_of_state_list(\n        equation_of_state_list_type &equation_of_state_list,\n        const std::string &subsection);\n\n  } // namespace EquationOfStateLibrary\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_aeos/equation_of_state_noble_abel_stiffened_gas.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"equation_of_state.h\"\n\nnamespace ryujin\n{\n  namespace EquationOfStateLibrary\n  {\n    /**\n     * The Noble-Abel-Stiffened gas equation of state\n     *\n     * @ingroup EulerEquations\n     */\n    class NobleAbelStiffenedGas : public EquationOfState\n    {\n    public:\n      using EquationOfState::pressure;\n      using EquationOfState::specific_internal_energy;\n      using EquationOfState::speed_of_sound;\n      using EquationOfState::temperature;\n\n      NobleAbelStiffenedGas(const std::string &subsection)\n          : EquationOfState(\"noble abel stiffened gas\", subsection)\n      {\n        gamma_ = 7. / 5.;\n        this->add_parameter(\"gamma\", gamma_, \"The ratio of specific heats\");\n\n        /*\n         * R is the specific gas constant with units [J / (Kg K)]. More details\n         * can be found at:\n         * https://en.wikipedia.org/wiki/Gas_constant#Specific_gas_constant\n         */\n        R_ = 287.052874;\n        this->add_parameter(\n            \"gas constant R\", R_, \"The specific gas constant R\");\n\n        cv_ = R_ / (gamma_ - 1.);\n\n        b_ = 0.;\n        this->add_parameter(\n            \"covolume b\", b_, \"The maximum compressibility constant\");\n\n        q_ = 0.;\n        this->add_parameter(\"reference specific internal energy\",\n                            q_,\n                            \"The reference specific internal energy\");\n\n        pinf_ = 0.;\n        this->add_parameter(\n            \"reference pressure\", pinf_, \"The reference pressure p infinity\");\n\n        /* Update the EOS interpolation parameters on parameter read in: */\n        ParameterAcceptor::parse_parameters_call_back.connect([this] {\n          this->interpolation_b_ = b_;\n          this->interpolation_pinfty_ = pinf_;\n          this->interpolation_q_ = q_;\n        });\n      }\n\n      /**\n       * The pressure is given by\n       * \\f{align}\n       *   p = (\\gamma - 1) \\rho (e - q) / (1 - b \\rho) - \\gamma p_\\infty\n       * \\f}\n       */\n      double pressure(double rho, double e) const final\n      {\n        return (gamma_ - 1.) * rho * (e - q_) / (1. - b_ * rho) -\n               gamma_ * pinf_;\n      }\n\n\n      /**\n       * The specific internal energy is given by\n       * \\f{align}\n       *   e - q = (p + \\gamma p_\\infty) * (1 - b \\rho) / (\\rho (\\gamma - 1))\n       * \\f}\n       */\n      double specific_internal_energy(double rho, double p) const final\n      {\n        const auto numerator = (p + gamma_ * pinf_) * (1. - b_ * rho);\n        const auto denominator = rho * (gamma_ - 1.);\n        return q_ + numerator / denominator;\n      }\n\n      /**\n       * The temperature is given by\n       * \\f{align}\n       *   T = (e - q - p_\\infty (1 / rho - b)) / c_v\n       * \\f}\n       */\n      double temperature(double rho, double e) const final\n      {\n        return (e - q_ - pinf_ * (1. / rho - b_)) / cv_;\n      }\n\n      /**\n       * Let \\f$X = (1 - b \\rho)\\f$. The speed of sound is given by\n       * \\f{align}\n       *   c^2 = \\frac{\\gamma (p + p_\\infty)}{\\rho X}\n       *       = \\frac{\\gamma (\\gamma -1)[\\rho (e - q) - p_\\infty X]}{\\rho X^2}\n       * \\f}\n       */\n      double speed_of_sound(double rho, double e) const final\n      {\n        const auto covolume = 1. - b_ * rho;\n        auto radicand =\n            (rho * (e - q_) - pinf_ * covolume) / (covolume * covolume * rho);\n        radicand *= gamma_ * (gamma_ - 1.);\n        return std::sqrt(radicand);\n      }\n\n    private:\n      double gamma_;\n      double R_;\n      double cv_;\n      double b_;\n      double q_;\n      double pinf_;\n    };\n  } // namespace EquationOfStateLibrary\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/euler_aeos/equation_of_state_polytropic_gas.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"equation_of_state.h\"\n\nnamespace ryujin\n{\n  namespace EquationOfStateLibrary\n  {\n    /**\n     * The polytropic gas equation of state\n     *\n     * @ingroup EulerEquations\n     */\n    class PolytropicGas : public EquationOfState\n    {\n    public:\n      using EquationOfState::pressure;\n      using EquationOfState::specific_internal_energy;\n      using EquationOfState::speed_of_sound;\n      using EquationOfState::temperature;\n\n      PolytropicGas(const std::string &subsection)\n          : EquationOfState(\"polytropic gas\", subsection)\n      {\n        gamma_ = 7. / 5.;\n        this->add_parameter(\"gamma\", gamma_, \"The ratio of specific heats\");\n\n        /*\n         * R is the specific gas constant with units [J / (Kg K)]. More details\n         * can be found at:\n         * https://en.wikipedia.org/wiki/Gas_constant#Specific_gas_constant\n         */\n        R_ = 287.052874;\n        this->add_parameter(\n            \"gas constant R\", R_, \"The specific gas constant R\");\n\n        cv_ = R_ / (gamma_ - 1.);\n      }\n\n      /**\n       * The pressure is given by\n       * \\f{align}\n       *   p = (\\gamma - 1) \\rho e\n       * \\f}\n       */\n      double pressure(double rho, double e) const final\n      {\n        return (gamma_ - 1.) * rho * e;\n      }\n\n      /**\n       * The specific internal energy is given by\n       * \\f{align}\n       *   e = p / (\\rho (\\gamma - 1))\n       * \\f}\n       */\n      double specific_internal_energy(double rho, double p) const final\n      {\n        return p / (rho * (gamma_ - 1.));\n      }\n\n      /**\n       * The temperature is given by\n       * \\f{align}\n       *   T = e / c_v\n       * \\f}\n       */\n      double temperature(double /*rho*/, double e) const final\n      {\n        return e / cv_;\n      }\n\n      /**\n       * The speed of sound is given by\n       * \\f{align}\n       *   c^2 = \\gamma * (\\gamma - 1) e\n       * \\f}\n       */\n      double speed_of_sound(double /*rho*/, double e) const final\n      {\n        return std::sqrt(gamma_ * (gamma_ - 1.) * e);\n      }\n\n    private:\n      double gamma_;\n      double R_;\n      double cv_;\n    };\n  } // namespace EquationOfStateLibrary\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_aeos/equation_of_state_pressureless.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2024 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"equation_of_state.h\"\n\nnamespace ryujin\n{\n  namespace EquationOfStateLibrary\n  {\n    /**\n     * Trivial equation of state for pressureless Euler\n     *\n     * @ingroup EulerEquations\n     */\n    class Pressureless : public EquationOfState\n    {\n    public:\n      using EquationOfState::pressure;\n      using EquationOfState::specific_internal_energy;\n      using EquationOfState::speed_of_sound;\n      using EquationOfState::temperature;\n\n      Pressureless(const std::string &subsection)\n          : EquationOfState(\"pressureless\", subsection)\n      {\n        gamma_ = 7. / 5.;\n        this->add_parameter(\"gamma\", gamma_, \"The ratio of specific heats\");\n      }\n\n      /**\n       * The pressure is given by\n       * \\f{align}\n       *   p = 0.0\n       *  \\f}\n       */\n      double pressure(double rho [[maybe_unused]],\n                      double e [[maybe_unused]]) const final\n      {\n        return 0.0;\n      }\n\n      /**\n       * The specific internal energy is given by\n       * \\f{align}\n       *   e = 0\n       * \\f}\n       */\n      double specific_internal_energy(double rho [[maybe_unused]],\n                                      double p [[maybe_unused]]) const final\n      {\n        return 0.0;\n      }\n\n      /**\n       * The temperature is given by\n       * \\f{align}\n       *   T = 0\n       * \\f}\n       */\n      double temperature(double /*rho*/, double e [[maybe_unused]]) const final\n      {\n        return 0.0;\n      }\n\n      /**\n       * The speed of sound is given by\n       * \\f{align}\n       *   c^2 = 0\n       * \\f}\n       */\n      double speed_of_sound(double /*rho*/,\n                            double e [[maybe_unused]]) const final\n      {\n        return 0.0;\n      }\n\n    private:\n      double gamma_;\n    };\n  } // namespace EquationOfStateLibrary\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_aeos/equation_of_state_sesame.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"equation_of_state.h\"\n#include \"lazy.h\"\n\n#include <deal.II/base/array_view.h>\n#include <deal.II/base/config.h>\n#include <deal.II/base/exceptions.h>\n#include <deal.II/base/parameter_acceptor.h>\n\n#ifdef WITH_EOSPAC\n#include \"eos_Interface.h\"\n#endif\n\n#include <filesystem>\n\n\nnamespace ryujin\n{\n#ifdef WITH_EOSPAC\n  /**\n   * A namespace with wrappers for the eospac6 library and sesame database. The\n   * eospac6 wrapper needs the file 'sesameFilesDir.txt' to be defined in the\n   * directory where you run your simulation. The file 'sesameFilesDir.txt'\n   * should list the path pointing to the sesame database. We refer the user to\n   * the EOSPAC manual for more information.\n   */\n  namespace eospac\n  {\n    /**\n     * A list of table types we support through our interface. This list is\n     * tailored to what we need for our EquationOfState interface and thus\n     * substantially shorter than what eospac6 supports.\n     */\n    enum class TableType : EOS_INTEGER {\n      /**\n       * (total) pressure [GPa] given as a function of density [Mg/m^3] and\n       * (total) specific internal energy [MJ/kg]\n       */\n      p_rho_e = EOS_Pt_DUt,\n      /**\n       * (total) specific internal energy [Mj/kg] as a function of density\n       * [Mg/m^3] and (total) pressure [GPa]\n       */\n      e_rho_p = EOS_Ut_DPt,\n    };\n\n    class Interface\n    {\n    public:\n      /**\n       * Constructor\n       *\n       * takes a vector consisting of tuples with material IDs and\n       * a corresponding TableType.\n       */\n      Interface(const std::vector<std::tuple<EOS_INTEGER, TableType>> &tables)\n      {\n        n_tables_ = tables.size();\n\n        std::transform(std::begin(tables),\n                       std::end(tables),\n                       std::back_inserter(material_ids_),\n                       [](const auto &it) { return std::get<0>(it); });\n\n        std::transform(std::begin(tables),\n                       std::end(tables),\n                       std::back_inserter(table_types_),\n                       [](const auto &it) {\n                         return static_cast<EOS_INTEGER>(std::get<1>(it));\n                       });\n\n        table_handles_.resize(n_tables_);\n\n        /* create tables: */\n\n        EOS_INTEGER error_code;\n        eos_CreateTables(&n_tables_,\n                         table_types_.data(),\n                         material_ids_.data(),\n                         table_handles_.data(),\n                         &error_code);\n        check_tables(\"eos_CreateTables\");\n\n        /* set table options: */\n\n        for (EOS_INTEGER i = 0; i < n_tables_; i++) {\n          // FIXME: refactor into options\n          eos_SetOption(\n              &table_handles_[i], &EOS_SMOOTH, EOS_NullPtr, &error_code);\n          check_error_code(error_code, \"eos_SetOption\", i);\n        }\n\n        /* load tables: */\n\n        eos_LoadTables(&n_tables_, table_handles_.data(), &error_code);\n        check_tables(\"eos_LoadTables\");\n      }\n\n      /**\n       * Desctructor\n       */\n      ~Interface() noexcept\n      {\n        EOS_INTEGER error_code;\n        eos_DestroyTables(&n_tables_, table_handles_.data(), &error_code);\n      }\n\n      /**\n       * Query the table with index @p index. The interpolation works on @p\n       * n tuples in parallel.\n       */\n      inline DEAL_II_ALWAYS_INLINE void\n      interpolate_values(const EOS_INTEGER &index,\n                         const dealii::ArrayView<EOS_REAL> &F,\n                         const dealii::ArrayView<EOS_REAL> &dFx,\n                         const dealii::ArrayView<EOS_REAL> &dFy,\n                         const dealii::ArrayView<const EOS_REAL> &X,\n                         const dealii::ArrayView<const EOS_REAL> &Y)\n      {\n        Assert(index >= 0 && index < n_tables_,\n               dealii::ExcMessage(\"Table index out of range\"));\n\n        EOS_INTEGER n_queries = F.size();\n\n#ifdef DEBUG\n        const decltype(dFx.size()) size = n_queries;\n        Assert(dFx.size() == size && dFy.size() == size && X.size() == size &&\n                   Y.size() == size,\n               dealii::ExcMessage(\"vector sizes do not match\"));\n#endif\n\n        EOS_INTEGER error_code;\n        eos_Interpolate(&table_handles_[index],\n                        &n_queries,\n                        const_cast<EOS_REAL *>(X.data()), /* sigh */\n                        const_cast<EOS_REAL *>(Y.data()), /* sigh */\n                        F.data(),\n                        dFx.data(),\n                        dFy.data(),\n                        &error_code);\n      }\n\n    private:\n      /**\n       * Parameters and handles for eos_createTables:\n       */\n      std::vector<EOS_INTEGER> material_ids_;\n      std::vector<EOS_INTEGER> table_types_;\n      EOS_INTEGER n_tables_;\n      /* Mutable so that we can call eospac functions from a const context. */\n      mutable std::vector<EOS_INTEGER> table_handles_;\n\n      /**\n       * Error handling:\n       */\n\n      void check_error_code(\n          EOS_INTEGER error_code,\n          const std::string &routine,\n          EOS_INTEGER i = std::numeric_limits<EOS_INTEGER>::max()) const\n      {\n        if (error_code != EOS_OK) {\n          std::array<EOS_CHAR, EOS_MaxErrMsgLen> error_message;\n          eos_GetErrorMessage(&error_code, error_message.data());\n\n          std::stringstream exception_body;\n          exception_body << \"Error: \" << routine;\n          if (i != std::numeric_limits<EOS_INTEGER>::max())\n            exception_body << \" (table \" << i << \")\";\n          exception_body << \": \" << error_code << \" - \" << error_message.data()\n                         << std::flush;\n          AssertThrow(false, dealii::ExcMessage(exception_body.str()));\n        }\n      }\n\n      void check_tables(const std::string &routine) const\n      {\n        for (EOS_INTEGER i = 0; i < n_tables_; i++) {\n          EOS_INTEGER table_error_code = EOS_OK;\n          eos_GetErrorCode(&table_handles_[i], &table_error_code);\n          if (table_error_code != EOS_OK) {\n            std::array<EOS_CHAR, EOS_MaxErrMsgLen> error_message;\n            eos_GetErrorMessage(&table_error_code, error_message.data());\n\n            std::stringstream exception_body;\n            exception_body << \"Error: \" << routine << \" (table \" << i\n                           << \"): \" << table_error_code << \" - \"\n                           << error_message.data() << std::flush;\n\n            AssertThrow(false, dealii::ExcMessage(exception_body.str()));\n          }\n        }\n      }\n    };\n  } // namespace eospac\n#endif\n\n  namespace EquationOfStateLibrary\n  {\n    /**\n     * A tabulated equation of state based on the EOSPAC6/Sesame\n     * database.\n     *\n     * Units are:\n     *        [rho] = kg / m^3\n     *          [p] = Pa = Kg / m / s^2\n     *          [e] = J / Kg = N m / Kg = m^2 / s^2\n     *\n     * @ingroup EulerEquations\n     */\n    class Sesame : public EquationOfState\n    {\n    public:\n      using EquationOfState::pressure;\n      using EquationOfState::specific_internal_energy;\n      using EquationOfState::speed_of_sound;\n      using EquationOfState::temperature;\n\n#ifdef WITH_EOSPAC\n      Sesame(const std::string &subsection)\n          : EquationOfState(\"sesame\", subsection)\n      {\n        material_id_ = 5030;\n        this->add_parameter(\n            \"material id\", material_id_, \"The Sesame Material ID\");\n      }\n\n\n      double pressure(double rho, double e) const final\n      {\n        eospac_guard_.ensure_initialized([&]() {\n          this->set_up_database();\n          return true;\n        });\n\n        EOS_INTEGER index = 0;\n\n        double p, p_drho, p_de;\n        const double rho_scaled = rho / 1.0e3; // convert from Kg/m^3 to Mg/m^3\n        const double e_scaled = e / 1.0e6;     // convert from J/kg to MJ/kg\n\n        eospac_interface_->interpolate_values(\n            index,\n            dealii::ArrayView<double>(&p, 1),\n            dealii::ArrayView<double>(&p_drho, 1),\n            dealii::ArrayView<double>(&p_de, 1),\n            dealii::ArrayView<const double>(&rho_scaled, 1),\n            dealii::ArrayView<const double>(&e_scaled, 1));\n\n        return 1.0e9 * p; // convert from GPa to Pa\n      }\n\n\n      void pressure(const dealii::ArrayView<double> &p,\n                    const dealii::ArrayView<double> &rho,\n                    const dealii::ArrayView<double> &e) const final\n      {\n        Assert(p.size() == rho.size() && rho.size() == e.size(),\n               dealii::ExcMessage(\"vectors have different size\"));\n\n        eospac_guard_.ensure_initialized([&]() {\n          this->set_up_database();\n          return true;\n        });\n\n        EOS_INTEGER index = 0;\n\n        /* FIXME: this is not reentrant... */\n        thread_local static std::vector<double> p_drho;\n        thread_local static std::vector<double> p_de;\n        p_drho.resize(p.size());\n        p_de.resize(p.size());\n\n        // convert from Kg/m^3 to Mg/m^3\n        std::transform(std::begin(rho),\n                       std::end(rho),\n                       std::begin(rho),\n                       [](double rho) { return rho / 1.0e3; });\n\n        // convert from J/kg to MJ/kg\n        std::transform(std::begin(e), //\n                       std::end(e),\n                       std::begin(e),\n                       [](auto e) { return e / 1.0e6; });\n\n        eospac_interface_->interpolate_values(index,\n                                              p,\n                                              dealii::ArrayView<double>(p_drho),\n                                              dealii::ArrayView<double>(p_de),\n                                              rho,\n                                              e);\n\n        // convert from GPa to Pa\n        std::transform(std::begin(p), //\n                       std::end(p),\n                       std::begin(p),\n                       [](auto it) { return it * 1.0e9; });\n      }\n\n\n      double specific_internal_energy(double rho, double p) const final\n      {\n        eospac_guard_.ensure_initialized([&]() {\n          this->set_up_database();\n          return true;\n        });\n\n        EOS_INTEGER index = 1;\n\n        double e, e_drho, e_dp;\n        const double rho_scaled = rho / 1.0e3; // convert from Kg/M^3 to Mg/M^3\n        const double p_scaled = p / 1.0e9;     // convert from Pa to GPa\n\n        eospac_interface_->interpolate_values(\n            index,\n            dealii::ArrayView<double>(&e, 1),\n            dealii::ArrayView<double>(&e_drho, 1),\n            dealii::ArrayView<double>(&e_dp, 1),\n            dealii::ArrayView<const double>(&rho_scaled, 1),\n            dealii::ArrayView<const double>(&p_scaled, 1));\n\n        return 1.0e6 * e; // convert from MJ/kg to J/kg\n      }\n\n\n      void\n      specific_internal_energy(const dealii::ArrayView<double> &e,\n                               const dealii::ArrayView<double> &rho,\n                               const dealii::ArrayView<double> &p) const final\n      {\n        Assert(e.size() == rho.size() && rho.size() == p.size(),\n               dealii::ExcMessage(\"vectors have different size\"));\n\n        eospac_guard_.ensure_initialized([&]() {\n          this->set_up_database();\n          return true;\n        });\n\n        EOS_INTEGER index = 1;\n\n        /* FIXME: this is not reentrant... */\n        thread_local static std::vector<double> e_drho;\n        thread_local static std::vector<double> e_dp;\n        e_drho.resize(e.size());\n        e_dp.resize(e.size());\n\n        // convert from Kg/m^3 to Mg/m^3\n        std::transform(std::begin(rho),\n                       std::end(rho),\n                       std::begin(rho),\n                       [](double rho) { return rho / 1.0e3; });\n\n        // convert from Pa to GPa\n        std::transform(std::begin(p), //\n                       std::end(p),\n                       std::begin(p),\n                       [](auto it) { return it / 1.0e9; });\n\n        eospac_interface_->interpolate_values(index,\n                                              e,\n                                              dealii::ArrayView<double>(e_drho),\n                                              dealii::ArrayView<double>(e_dp),\n                                              rho,\n                                              p);\n\n        // convert from MJ/kg to J/kg\n        std::transform(std::begin(e), //\n                       std::end(e),\n                       std::begin(e),\n                       [](auto e) { return e * 1.0e6; });\n      }\n\n      /* FIXME: Implement table look up for temperature. Need to think about\n       * whether it should be T(rho, e) or T(rho, p). */\n\n      double temperature(double /*rho*/, double /*e*/) const final\n      {\n        eospac_guard_.ensure_initialized([&]() {\n          this->set_up_database();\n          return true;\n        });\n\n        AssertThrow(false, dealii::ExcInternalError());\n        __builtin_trap();\n      }\n\n\n      void temperature(const dealii::ArrayView<double> & /*temp*/,\n                       const dealii::ArrayView<double> & /*rho*/,\n                       const dealii::ArrayView<double> & /*e*/) const final\n      {\n        eospac_guard_.ensure_initialized([&]() {\n          this->set_up_database();\n          return true;\n        });\n\n        AssertThrow(false, dealii::ExcInternalError());\n        __builtin_trap();\n      }\n\n\n      double speed_of_sound(double /*rho*/, double /*e*/) const final\n      {\n        eospac_guard_.ensure_initialized([&]() {\n          this->set_up_database();\n          return true;\n        });\n\n        AssertThrow(false, dealii::ExcInternalError());\n        __builtin_trap();\n      }\n\n\n      void speed_of_sound(const dealii::ArrayView<double> & /*c*/,\n                          const dealii::ArrayView<double> & /*rho*/,\n                          const dealii::ArrayView<double> & /*e*/) const final\n      {\n        eospac_guard_.ensure_initialized([&]() {\n          this->set_up_database();\n          return true;\n        });\n\n        AssertThrow(false, dealii::ExcInternalError());\n        __builtin_trap();\n      }\n\n    private:\n      /**\n       * Private methods and fields for initializing the eospac interface\n       */\n      //@{\n      //\n      void set_up_database() const\n      {\n        AssertThrow(\n            std::filesystem::exists(\"sesameFilesDir.txt\"),\n            dealii::ExcMessage(\n                \"For EOSPAC to find the sesame database, we assume that there \"\n                \"exists a file named 'sesameFilesDir.txt' in the current \"\n                \"simulation directory. This file should list the path to the \"\n                \"sesame database. See the EOSPAC manual for more \"\n                \"information.\"));\n        const std::vector<std::tuple<EOS_INTEGER, eospac::TableType>> tables{\n            {material_id_, eospac::TableType::p_rho_e},\n            {material_id_, eospac::TableType::e_rho_p},\n        };\n\n        eospac_interface_ = std::make_unique<eospac::Interface>(tables);\n      }\n\n      Lazy<bool> eospac_guard_;\n      mutable std::unique_ptr<eospac::Interface> eospac_interface_;\n\n      //@}\n      /**\n       * @name Run time options\n       */\n      //@{\n\n      EOS_INTEGER material_id_;\n\n      //@}\n\n#else /* WITHOUT_EOSPAC */\n\n      /* We do not have eospac support */\n      Sesame(const std::string &subsection)\n          : EquationOfState(\"Sesame\", subsection)\n      {\n      }\n\n      static constexpr auto message =\n          \"ryujin has to be configured with eospac support in order to use \"\n          \"the Sesame EOS database\";\n\n      double pressure(double /*rho*/, double /*internal_energy*/) const final\n      {\n        AssertThrow(false, dealii::ExcMessage(message));\n        __builtin_trap();\n      }\n\n      double specific_internal_energy(double /*rho*/, double /*p*/) const final\n      {\n        AssertThrow(false, dealii::ExcMessage(message));\n        __builtin_trap();\n      }\n\n      double temperature(double /*rho*/, double /*e*/) const final\n      {\n        AssertThrow(false, dealii::ExcMessage(message));\n        __builtin_trap();\n      }\n\n      double speed_of_sound(double /*rho*/, double /*e*/) const final\n      {\n        AssertThrow(false, dealii::ExcMessage(message));\n        __builtin_trap();\n      }\n#endif\n    };\n  } // namespace EquationOfStateLibrary\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_aeos/equation_of_state_simple_macaw.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// [LANL Copyright Statement]\n// Copyright (C) 2025 by the ryujin authors\n// Copyright (C) 2024 - 2025 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include \"equation_of_state.h\"\n#include <cmath>\n\nnamespace ryujin\n{\n  namespace EquationOfStateLibrary\n  {\n    /**\n     * The simple MACAW equation of state. See:\n     * \"A Simple MACAW Equation of State\" by Aslam and Lozano (LA-UR-24-32805).\n     * This is a \"simple\" thermodynamically consistent equation of state.\n     * The default parameters are taken from Table 1 in reference and were used\n     * in calibrating a model for copper.\n     *\n     * @ingroup EulerEquations\n     */\n    class SimpleMacaw : public EquationOfState\n    {\n    public:\n      using EquationOfState::pressure;\n      using EquationOfState::specific_internal_energy;\n      using EquationOfState::speed_of_sound;\n      using EquationOfState::temperature;\n\n      SimpleMacaw(const std::string &subsection)\n          : EquationOfState(\"simple macaw\", subsection)\n      {\n        rho0_ = 8.952; // [g/cc]\n        this->add_parameter(\n            \"reference rho0\", rho0_, \"The reference density at T=0 and P=0\");\n\n        T0_ = 150.; // [K]\n        this->add_parameter(\"reference T0\", T0_, \"The reference temperature\");\n\n        Gc_ = 0.5; // [unitless]\n        this->add_parameter(\"Gamma\", Gc_, \"The Gruneisen parameter\");\n\n        capA_ = 7.3; // [Gpa]. This is the \"bulk modulus divided by B\"\n        this->add_parameter(\"A\", capA_, \"The A constant\");\n\n        // Derivative of the bulk modulus w.r.t pressure at T=0, P=0.\n        capB_ = 3.9; // [unitless]\n        this->add_parameter(\"B\", capB_, \"The B constant\");\n\n        // The Dulong-Petit limit of the specific heat at constant volume\n        cvInf_ = 3.89e-4; // [kJ / (g K )]\n        this->add_parameter(\"cvInf\", cvInf_, \"The Dulong-Petit limit of cv\");\n\n        /*\n         * Update the EOS interpolation parameters on parameter read in and\n         * reference volume:\n         */\n        const auto update_values = [this]() {\n          this->interpolation_pinfty_ = capA_ * capB_;\n          v0_ = 1. / rho0_;\n        };\n\n        this->parse_parameters_call_back.connect(update_values);\n        update_values();\n      }\n\n      /**\n       * The pressure is given by the formula (19) in reference.\n       */\n      double pressure(double rho, double e) const final\n      {\n        const auto v = 1. / rho;\n\n        const auto p_cold = pressure_cold(v);\n        const auto e_cold = energy_cold(v);\n\n        return p_cold + Gc_ * rho * (e - e_cold);\n      }\n\n      /**\n       * The specific internal energy is given by a complicated formula.\n       */\n      double specific_internal_energy(double rho, double p) const final\n      {\n        const auto v = 1. / rho;\n\n        const auto p_cold = pressure_cold(v);\n        const auto e_cold = energy_cold(v);\n\n        return (p - p_cold) / (Gc_ * rho) + e_cold;\n      }\n\n      /**\n       * The temperature is given by the formla (18) in reference.\n       */\n      double temperature(double rho, double e) const final\n      {\n        const auto v = 1. / rho;\n        const auto ratio = v / v0_;\n\n        const auto delta_e = e - energy_cold(v);\n        const auto radicand =\n            delta_e * (delta_e + 4. * cvInf_ * T0_ * std::pow(ratio, -Gc_));\n        const auto numerator = delta_e + std::sqrt(radicand);\n        const auto denominator = 2. * cvInf_;\n\n        return numerator / denominator;\n      }\n\n      /**\n       * The speed of sound is given by equation (21) in reference.\n       */\n      double speed_of_sound(double rho, double e) const final\n      {\n        const auto v = 1. / rho;\n        const auto ratio = v / v0_;\n\n        const auto e_cold = energy_cold(v);\n\n        // Cold contribution\n        const auto c_cold =\n            capA_ * capB_ * v0_ * (capB_ + 1.) * std::pow(ratio, -capB_);\n\n        return std::sqrt(c_cold + Gc_ * (Gc_ + 1.) * (e - e_cold));\n      }\n\n    private:\n      double rho0_;\n      double v0_;\n      double T0_;\n      double capA_;\n      double capB_;\n      double Gc_;\n      double cvInf_;\n\n      double pressure_cold(const double v) const\n      {\n        const auto ratio = v / v0_;\n\n        auto cold_curve = std::pow(ratio, -capB_ - 1.) - 1.;\n        cold_curve *= capA_ * capB_;\n        return cold_curve;\n      }\n\n      double energy_cold(const double v) const\n      {\n        const auto ratio = v / v0_;\n\n        auto e_cold = std::pow(ratio, -capB_) + ratio * capB_ - (capB_ + 1.);\n        e_cold *= capA_ * v0_;\n\n        return e_cold;\n      }\n\n      double specific_entropy(const double v, const double T) const\n      {\n        const auto ratio = v / v0_;\n        const auto theta = T0_ * std::pow(ratio, -Gc_);\n        const auto tau = T / theta;\n        return cvInf_ * (tau / (1. + tau) + std::log(1. + tau));\n      }\n    };\n  } // namespace EquationOfStateLibrary\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_aeos/equation_of_state_van_der_waals.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"equation_of_state.h\"\n\nnamespace ryujin\n{\n  namespace EquationOfStateLibrary\n  {\n    /**\n     * The Van der Waals equation of state\n     *\n     * @ingroup EulerEquations\n     */\n    class VanDerWaals : public EquationOfState\n    {\n    public:\n      using EquationOfState::pressure;\n      using EquationOfState::specific_internal_energy;\n      using EquationOfState::speed_of_sound;\n      using EquationOfState::temperature;\n\n      VanDerWaals(const std::string &subsection)\n          : EquationOfState(\"van der waals\", subsection)\n      {\n        gamma_ = 7. / 5.;\n        this->add_parameter(\"gamma\", gamma_, \"The ratio of specific heats\");\n\n        a_ = 0.;\n        this->add_parameter(\"vdw a\", a_, \"The vdw a constant\");\n\n        b_ = 0.;\n        this->add_parameter(\n            \"covolume b\", b_, \"The maximum compressibility constant\");\n\n        /*\n         * R is the specific gas constant with units [J / (Kg K)]. More details\n         * can be found at:\n         * https://en.wikipedia.org/wiki/Gas_constant#Specific_gas_constant\n         */\n        R_ = 0.4;\n        this->add_parameter(\n            \"gas constant R\", R_, \"The specific gas constant R\");\n\n        cv_ = R_ / (gamma_ - 1.);\n\n        /* Update the EOS interpolation parameters on parameter read in: */\n        ParameterAcceptor::parse_parameters_call_back.connect([this] {\n          this->interpolation_b_ = b_;\n          /*\n           * FIXME: The van der Waals EOS allows for negative pressures. We\n           * should thus come up with a sensible way of setting\n           * \"interpolation_pinfty_\"...\n           */\n        });\n      }\n\n      /**\n       * The pressure is given by\n       * \\f{align}\n       *   p = (\\gamma - 1) * (\\rho * e + a \\rho^2)/(1 - b \\rho) - a \\rho^2\n       * \\f}\n       */\n      double pressure(double rho, double e) const final\n      {\n        const auto intermolecular = a_ * rho * rho;\n        const auto numerator = rho * e + intermolecular;\n        const auto covolume = 1. - b_ * rho;\n        return (gamma_ - 1.) * numerator / covolume - intermolecular;\n      }\n\n      /**\n       * The specific internal energy is given by\n       * \\f{align}\n       *   e = (p + a \\rho^2) * (1 - b \\rho) / (\\rho (\\gamma -1)) - a \\rho\n       * \\f}\n       */\n      double specific_internal_energy(double rho, double p) const final\n      {\n        const auto intermolecular = a_ * rho * rho;\n        const auto covolume = 1. - b_ * rho;\n        const auto numerator = (p + intermolecular) * covolume;\n        const auto denominator = rho * (gamma_ - 1.);\n        return numerator / denominator - a_ * rho;\n      }\n\n      /**\n       * The temperature is given by\n       * \\f{align}\n       *   T = (\\gamma - 1) / R (e + a \\rho)\n       * \\f}\n       */\n      double temperature(double rho, double e) const final\n      {\n        return (e + a_ * rho) / cv_;\n      }\n\n      /**\n       * The speed of sound is given by\n       * \\f{align}\n       *   c^2 = \\frac{\\gamma (\\gamma -1) (e + a \\rho)}{(1 - b\\rho)^2}\n       *   - 2a\\rho.\n       * \\f}\n       */\n      double speed_of_sound(double rho, double e) const final\n      {\n        const auto covolume = 1. - b_ * rho;\n        const auto numerator = gamma_ * (gamma_ - 1.) * (e + a_ * rho);\n        return std::sqrt(numerator / (covolume * covolume) - 2. * a_ * rho);\n      }\n\n    private:\n      double gamma_;\n      double b_;\n      double a_;\n      double R_;\n      double cv_;\n    };\n  } // namespace EquationOfStateLibrary\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/euler_aeos/hyperbolic_system.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"equation_of_state_library.h\"\n\n#include <convenience_macros.h>\n#include <discretization.h>\n#include <loop.h>\n#include <multicomponent_vector.h>\n#include <patterns_conversion.h>\n#include <simd.h>\n#include <state_vector.h>\n\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/tensor.h>\n\n#include <array>\n\nnamespace ryujin\n{\n  namespace EulerAEOS\n  {\n    /*\n     * For various divisions in the arbirtray equation of state module we\n     * have a mathematical guarantee that the numerator and denominator are\n     * nonnegative and the limit (of zero numerator and denominator) must\n     * converge to zero. The following function takes care of rounding\n     * issues when computing such quotients by (a) avoiding division by\n     * zero and (b) ensuring non-negativity of the result.\n     */\n    template <typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number safe_division(const Number &numerator,\n                                                      const Number &denominator)\n    {\n      using ScalarNumber = typename get_value_type<Number>::type;\n      constexpr ScalarNumber min = std::numeric_limits<ScalarNumber>::min();\n\n      return std::max(numerator, Number(0.)) /\n             std::max(denominator, Number(min));\n    }\n\n\n    template <int dim, typename Number>\n    class HyperbolicSystemView;\n\n    /**\n     * The compressible Euler equations of gas dynamics. Generalized\n     * implementation with a modified approximate Riemann solver for\n     * finding max wave speed, indicator, and limiter suitable for\n     * arbitrary equations of state.\n     *\n     * We have a (2 + dim) dimensional state space \\f$[\\rho, \\textbf m,\n     * E]\\f$, where \\f$\\rho\\f$ denotes the density, \\f$\\textbf m\\f$ is the\n     * momentum, and \\f$E\\f$ is the total energy.\n     *\n     * @ingroup EulerEquations\n     */\n    class HyperbolicSystem final : public dealii::ParameterAcceptor\n    {\n    public:\n      /**\n       * The name of the hyperbolic system as a string.\n       */\n      static inline std::string problem_name =\n          \"Compressible Euler equations (arbitrary EOS)\";\n\n      /**\n       * Constructor.\n       */\n      HyperbolicSystem(const std::string &subsection = \"/HyperbolicSystem\");\n\n      /**\n       * Return a view on the Hyperbolic System for a given dimension @p\n       * dim and choice of number type @p Number (which can be a scalar\n       * float, or double, as well as a VectorizedArray holding packed\n       * scalars.\n       */\n      template <int dim, typename Number>\n      auto view() const\n      {\n        return HyperbolicSystemView<dim, Number>{*this};\n      }\n\n      /**\n       * Part of step 1 of the hyperbolic update step: Compute \"precomputed\n       * values\" and fill into the state vector.\n       *\n       * @note The method does not update the ghost range of the state\n       * vector. The precomputed part has to be synchronized by explicitly\n       * calling the update ghost values function.\n       */\n      template <int dim, typename ScalarNumber>\n      void fill_precomputed_values(\n          const OfflineData<dim, ScalarNumber> &offline_data,\n          typename HyperbolicSystemView<dim, ScalarNumber>::StateVector\n              &state_vector,\n          const bool skip_constrained_dofs = true) const;\n\n    private:\n      /**\n       * @name Runtime parameters, internal fields, methods, and friends\n       */\n      //@{\n\n      std::string equation_of_state_;\n      double reference_density_;\n      double vacuum_state_relaxation_small_;\n      double vacuum_state_relaxation_large_;\n      bool compute_strict_bounds_;\n\n      EquationOfStateLibrary::equation_of_state_list_type\n          equation_of_state_list_;\n\n      using EquationOfState = EquationOfStateLibrary::EquationOfState;\n      std::shared_ptr<EquationOfState> selected_equation_of_state_;\n\n      template <int dim, typename Number>\n      friend class HyperbolicSystemView;\n      //@}\n    }; /* HyperbolicSystem */\n\n\n    /**\n     * A view of the HyperbolicSystem that makes methods available for a\n     * given dimension @p dim and choice of number type @p Number (which\n     * can be a scalar float, or double, as well as a VectorizedArray\n     * holding packed scalars.\n     *\n     * Intended usage:\n     * ```\n     * HyperbolicSystem hyperbolic_system;\n     * const auto view = hyperbolic_system.template view<dim, Number>();\n     * const auto flux_i = view.flux_contribution(...);\n     * const auto flux_j = view.flux_contribution(...);\n     * const auto flux_ij = view.flux_divergence(flux_i, flux_j, c_ij);\n     * // etc.\n     * ```\n     */\n    template <int dim, typename Number>\n    class HyperbolicSystemView\n    {\n    public:\n      /**\n       * Constructor taking a reference to the underlying\n       * HyperbolicSystem\n       */\n      HyperbolicSystemView(const HyperbolicSystem &hyperbolic_system)\n          : hyperbolic_system_(hyperbolic_system)\n      {\n      }\n\n      /**\n       * Create a modified view from the current one:\n       */\n      template <int dim2, typename Number2>\n      auto view() const\n      {\n        return HyperbolicSystemView<dim2, Number2>{hyperbolic_system_};\n      }\n\n      /**\n       * The underlying scalar number type.\n       */\n      using ScalarNumber = typename get_value_type<Number>::type;\n\n      /**\n       * @name Access to runtime parameters\n       */\n      //@{\n\n      DEAL_II_ALWAYS_INLINE inline const std::string &equation_of_state() const\n      {\n        return hyperbolic_system_.equation_of_state_;\n      }\n\n      DEAL_II_ALWAYS_INLINE inline ScalarNumber reference_density() const\n      {\n        return hyperbolic_system_.reference_density_;\n      }\n\n      DEAL_II_ALWAYS_INLINE inline ScalarNumber\n      vacuum_state_relaxation_small() const\n      {\n        return hyperbolic_system_.vacuum_state_relaxation_small_;\n      }\n\n      DEAL_II_ALWAYS_INLINE inline ScalarNumber\n      vacuum_state_relaxation_large() const\n      {\n        return hyperbolic_system_.vacuum_state_relaxation_large_;\n      }\n\n      DEAL_II_ALWAYS_INLINE inline bool compute_strict_bounds() const\n      {\n        return hyperbolic_system_.compute_strict_bounds_;\n      }\n\n      //@}\n      /**\n       * @name Low-level access to the selected equation of state.\n       */\n      //@{\n\n      /**\n       * For a given density \\f$\\rho\\f$ and <i>specific</i> internal\n       * energy \\f$e\\f$ return the pressure \\f$p\\f$.\n       */\n      DEAL_II_ALWAYS_INLINE inline Number eos_pressure(const Number &rho,\n                                                       const Number &e) const\n      {\n        const auto &eos = hyperbolic_system_.selected_equation_of_state_;\n\n        if constexpr (std::is_same_v<ScalarNumber, Number>) {\n          return ScalarNumber(eos->pressure(rho, e));\n        } else {\n          Number p;\n          for (unsigned int k = 0; k < Number::size(); ++k) {\n            p[k] = ScalarNumber(eos->pressure(rho[k], e[k]));\n          }\n          return p;\n        }\n      }\n\n      /**\n       * For a given density \\f$\\rho\\f$ and pressure \\f$p\\f$ return the\n       * <i>specific</i> internal energy \\f$e\\f$.\n       */\n      DEAL_II_ALWAYS_INLINE inline Number\n      eos_specific_internal_energy(const Number &rho, const Number &p) const\n      {\n        const auto &eos = hyperbolic_system_.selected_equation_of_state_;\n\n        if constexpr (std::is_same_v<ScalarNumber, Number>) {\n          return ScalarNumber(eos->specific_internal_energy(rho, p));\n        } else {\n          Number e;\n          for (unsigned int k = 0; k < Number::size(); ++k) {\n            e[k] = ScalarNumber(eos->specific_internal_energy(rho[k], p[k]));\n          }\n          return e;\n        }\n      }\n\n      /**\n       * For a given density \\f$\\rho\\f$ and specific internal energy \\f$e\\f$\n       * return the temperature \\f$T\\f$.\n       */\n      DEAL_II_ALWAYS_INLINE inline Number eos_temperature(const Number &rho,\n                                                          const Number &e) const\n      {\n        const auto &eos = hyperbolic_system_.selected_equation_of_state_;\n\n        if constexpr (std::is_same_v<ScalarNumber, Number>) {\n          return ScalarNumber(eos->temperature(rho, e));\n        } else {\n          Number temp;\n          for (unsigned int k = 0; k < Number::size(); ++k) {\n            temp[k] = ScalarNumber(eos->temperature(rho[k], e[k]));\n          }\n          return temp;\n        }\n      }\n\n      /**\n       * For a given density \\f$\\rho\\f$ and <i>specific</i> internal\n       * energy \\f$e\\f$ return the sound speed \\f$a\\f$.\n       */\n      DEAL_II_ALWAYS_INLINE inline Number\n      eos_speed_of_sound(const Number &rho, const Number &e) const\n      {\n        const auto &eos = hyperbolic_system_.selected_equation_of_state_;\n\n        if constexpr (std::is_same_v<ScalarNumber, Number>) {\n          return ScalarNumber(eos->speed_of_sound(rho, e));\n        } else {\n          Number c;\n          for (unsigned int k = 0; k < Number::size(); ++k) {\n            c[k] = ScalarNumber(eos->speed_of_sound(rho[k], e[k]));\n          }\n          return c;\n        }\n      }\n\n      /**\n       * Return the interpolatory covolume \\f$b_{\\text{interp}}\\f$.\n       */\n      DEAL_II_ALWAYS_INLINE inline ScalarNumber eos_interpolation_b() const\n      {\n        const auto &eos = hyperbolic_system_.selected_equation_of_state_;\n        return ScalarNumber(eos->interpolation_b());\n      }\n\n      /**\n       * Return the interpolatory reference pressure \\f$p_{\\infty}\\f$.\n       */\n      DEAL_II_ALWAYS_INLINE inline ScalarNumber eos_interpolation_pinfty() const\n      {\n        const auto &eos = hyperbolic_system_.selected_equation_of_state_;\n        return ScalarNumber(eos->interpolation_pinfty());\n      }\n\n      /**\n       * Return the interpolatory reference specific internal energy\n       * \\f$q\\f$.\n       */\n      DEAL_II_ALWAYS_INLINE inline ScalarNumber eos_interpolation_q() const\n      {\n        const auto &eos = hyperbolic_system_.selected_equation_of_state_;\n        return ScalarNumber(eos->interpolation_q());\n      }\n\n      //@}\n      /**\n       * constexpr booleans used in the EulerInitialStates namespace\n       */\n      //@{\n\n      static constexpr bool have_gamma = false;\n      static constexpr bool have_eos_interpolation_b = true;\n      static constexpr bool have_energy_equation = true;\n\n      //@}\n      /**\n       * @name Internal data\n       */\n      //@{\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n    public:\n      //@}\n      /**\n       * @name Types and constexpr constants\n       */\n      //@{\n\n      /**\n       * The dimension of the state space.\n       */\n      static constexpr unsigned int problem_dimension = 2 + dim;\n\n      /**\n       * Storage type for a (conserved) state vector \\f$\\boldsymbol U\\f$.\n       */\n      using state_type = dealii::Tensor<1, problem_dimension, Number>;\n\n      /**\n       * Storage type for the flux \\f$\\mathbf{f}\\f$.\n       */\n      using flux_type =\n          dealii::Tensor<1, problem_dimension, dealii::Tensor<1, dim, Number>>;\n\n      /**\n       * The storage type used for flux contributions.\n       */\n      using flux_contribution_type = flux_type;\n\n      /**\n       * An array holding all component names of the conserved state as a\n       * string.\n       */\n      static inline const auto component_names =\n          []() -> std::array<std::string, problem_dimension> {\n        if constexpr (dim == 1)\n          return {\"rho\", \"m\", \"E\"};\n        else if constexpr (dim == 2)\n          return {\"rho\", \"m_1\", \"m_2\", \"E\"};\n        else if constexpr (dim == 3)\n          return {\"rho\", \"m_1\", \"m_2\", \"m_3\", \"E\"};\n        __builtin_trap();\n      }();\n\n      /**\n       * An array holding all component names of the primitive state as a\n       * string.\n       */\n      static inline const auto primitive_component_names =\n          []() -> std::array<std::string, problem_dimension> {\n        if constexpr (dim == 1)\n          return {\"rho\", \"v\", \"e\"};\n        else if constexpr (dim == 2)\n          return {\"rho\", \"v_1\", \"v_2\", \"e\"};\n        else if constexpr (dim == 3)\n          return {\"rho\", \"v_1\", \"v_2\", \"v_3\", \"e\"};\n        __builtin_trap();\n      }();\n\n      /**\n       * The number of precomputed values.\n       */\n      static constexpr unsigned int n_precomputed_values = 4;\n\n      /**\n       * Array type used for precomputed values.\n       */\n      using precomputed_type = std::array<Number, n_precomputed_values>;\n\n      /**\n       * An array holding all component names of the precomputed values.\n       */\n      static inline const auto precomputed_names =\n          std::array<std::string, n_precomputed_values>{\n              {\"p\",\n               \"surrogate_gamma_min\",\n               \"surrogate_specific_entropy\",\n               \"surrogate_harten_entropy\"}};\n\n      /**\n       * The number of precomputed initial values.\n       */\n      static constexpr unsigned int n_initial_precomputed_values = 0;\n\n      /**\n       * Array type used for precomputed initial values.\n       */\n      using initial_precomputed_type =\n          std::array<Number, n_initial_precomputed_values>;\n\n      /**\n       * An array holding all component names of the precomputed values.\n       */\n      static inline const auto initial_precomputed_names =\n          std::array<std::string, n_initial_precomputed_values>{};\n\n      /**\n       * A compound state vector.\n       */\n      using StateVector = Vectors::\n          StateVector<ScalarNumber, problem_dimension, n_precomputed_values>;\n\n      /**\n       * MulticomponentVector for storing the hyperbolic state vector:\n       */\n      using HyperbolicVector =\n          Vectors::MultiComponentVector<ScalarNumber, problem_dimension>;\n\n      /**\n       * MulticomponentVector for storing a vector of precomputed states:\n       */\n      using PrecomputedVector =\n          Vectors::MultiComponentVector<ScalarNumber, n_precomputed_values>;\n\n      /**\n       * MulticomponentVector for storing a vector of precomputed initial\n       * states:\n       */\n      using InitialPrecomputedVector =\n          Vectors::MultiComponentVector<ScalarNumber,\n                                        n_initial_precomputed_values>;\n\n      //@}\n      /**\n       * @name Computing derived physical quantities\n       */\n      //@{\n\n      /**\n       * For a given (2+dim dimensional) state vector <code>U</code>, return\n       * the density <code>U[0]</code>\n       */\n      static Number density(const state_type &U);\n\n      /**\n       * Given a density @p rho this function returns 0 if the magniatude\n       * of rho is smaller or equal than relaxation_large * rho_cutoff.\n       * Otherwise rho is returned unmodified. Here, rho_cutoff is the\n       * reference density multiplied by eps.\n       */\n      Number filter_vacuum_density(const Number &rho) const;\n\n      /**\n       * For a given (2+dim dimensional) state vector <code>U</code>, return\n       * the momentum vector <code>[U[1], ..., U[1+dim]]</code>.\n       */\n      static dealii::Tensor<1, dim, Number> momentum(const state_type &U);\n\n      /**\n       * For a given (2+dim dimensional) state vector <code>U</code>, return\n       * the total energy <code>U[1+dim]</code>\n       */\n      static Number total_energy(const state_type &U);\n\n      /**\n       * For a given (2+dim dimensional) state vector <code>U</code>, compute\n       * and return the internal energy \\f$\\varepsilon = (\\rho e)\\f$.\n       */\n      static Number internal_energy(const state_type &U);\n\n      /**\n       * For a given (2+dim dimensional) state vector <code>U</code>, compute\n       * and return the derivative of the internal energy\n       * \\f$\\varepsilon = (\\rho e)\\f$.\n       */\n      static state_type internal_energy_derivative(const state_type &U);\n\n      //@}\n      /**\n       * @name Surrogate functions for computing various interpolatory\n       * physical quantities that are needed for Riemann solver,\n       * indicator and limiter.\n       */\n      //@{\n\n      /**\n       * For a given (2+dim dimensional) state vector <code>U</code>, compute\n       * and return a (scaled) surrogate specific entropy\n       * \\f[\n       *   e^{(\\gamma_{\\text{min}} - 1)s} =\n       *   \\frac{\\rho\\,(e-q)-p_{\\infty}(1-b\\rho)}{\\rho^\\gamma_{\\text{min}}}\n       *   (1 - b\\,\\rho)^{\\gamma_{\\text{min}} -1}.\n       * \\f]\n       */\n      Number surrogate_specific_entropy(const state_type &U,\n                                        const Number &gamma_min) const;\n\n      /**\n       * For a given (2+dim dimensional) state vector <code>U</code>, compute\n       * and return a surrogate Harten-type entropy\n       * \\f[\n       *   \\eta =\n       *   (1-b\\,\\rho)^{\\frac{\\gamma_{\\text{min}-1}}{\\gamma_{\\text{min}}+1}}\n       *   \\big(\\rho^2 (e-q) - \\rho p_{\\infty}(1-b\\,\\rho)\\big)\n       *   ^{1/(\\gamma_{\\text{min}}+1)}\n       * \\f]\n       */\n      Number surrogate_harten_entropy(const state_type &U,\n                                      const Number &gamma_min) const;\n\n      /**\n       * For a given (2+dim dimensional) state vector <code>U</code>, compute\n       * and return the derivative \\f$\\eta'\\f$ of the Harten-type entropy\n       * \\f[\n       *   \\eta =\n       *   (1-b\\,\\rho)^{\\frac{\\gamma_{\\text{min}-1}}{\\gamma_{\\text{min}}+1}}\n       *   \\big(\\rho^2 (e-q) - \\rho p_{\\infty}(1-b\\,\\rho)\\big)\n       *   ^{1/(\\gamma_{\\text{min}}+1)}\n       * \\f]\n       */\n      state_type\n      surrogate_harten_entropy_derivative(const state_type &U,\n                                          const Number &eta,\n                                          const Number &gamma_min) const;\n\n      /**\n       * For a given (2+dim dimensional) state vector <code>U</code> and\n       * pressure <code>p</code>, compute a surrogate gamma:\n       * \\f[\n       *   \\gamma(\\rho, e, p) = 1 + \\frac{(p + p_{\\infty})(1 - b \\rho)}\n       *   {\\rho (e-q) - p_{\\infty}(1-b \\rho)}\n       * \\f]\n       *\n       * This function is used in various places to create interpolations\n       * of the pressure.\n       */\n      Number surrogate_gamma(const state_type &U, const Number &p) const;\n\n      /**\n       * For a given (2+dim dimensional) state vector <code>U</code> and\n       * gamma <code>gamma</code>, compute a surrogate pressure:\n       * \\f[\n       *   p(\\rho, e, \\gamma) = (\\gamma - 1) \\frac{\\rho (e - q)}{1 - b \\rho}\n       *   -\\gamma\\,p_{\\infty}\n       * \\f]\n       *\n       * This function is the complementary function to surrogate_gamma(),\n       * meaning the following property holds true:\n       * ```\n       *   surrogate_gamma(U, surrogate_pressure(U, gamma)) == gamma\n       *       surrogate_pressure(U, surrogate_gamma(U, p)) == p\n       * ```\n       */\n      Number surrogate_pressure(const state_type &U, const Number &gamma) const;\n\n      /**\n       * For a given (2+dim dimensional) state vector <code>U</code> and\n       * gamma <code>gamma</code>, compute a surrogate speed of sound:\n       * \\f{align}\n       *   c^2(\\rho, e, \\gamma) = \\frac{\\gamma (p + p_\\infty)}{\\rho X}\n       *       = \\frac{\\gamma (\\gamma -1)[\\rho (e - q) - p_\\infty X]}{\\rho X^2}\n       * \\f}\n       */\n      Number surrogate_speed_of_sound(const state_type &U,\n                                      const Number &gamma) const;\n\n      /**\n       * Returns whether the state @p U is admissible. If @p U is a\n       * vectorized state then @p U is admissible if all vectorized values\n       * are admissible.\n       */\n      bool is_admissible(const state_type &U) const;\n\n      //@}\n      /**\n       * @name Special functions for boundary states\n       */\n      //@{\n\n      /**\n       * Decomposes a given state @p U into Riemann invariants and then\n       * replaces the first or second Riemann characteristic from the one\n       * taken from @p U_bar state. Note that the @p U_bar state is just the\n       * prescribed dirichlet values.\n       */\n      template <int component>\n      state_type prescribe_riemann_characteristic(\n          const state_type &U,\n          const Number &p,\n          const state_type &U_bar,\n          const Number &p_bar,\n          const dealii::Tensor<1, dim, Number> &normal) const;\n\n      /**\n       * Apply boundary conditions.\n       *\n       * For the compressible Euler equations we have:\n       *\n       *  - Dirichlet boundary conditions by prescribing the return value of\n       *    get_dirichlet_data() as is.\n       *\n       *  - Slip boundary conditions where we remove the normal component of\n       *    the momentum.\n       *\n       *  - No slip boundary conditions where we set the momentum to 0.\n       *\n       *  - \"Dynamic boundary\" conditions that prescribe different Riemann\n       *    invariants from the return value of get_dirichlet_data()\n       *    depending on the flow state (supersonic versus subsonic, outflow\n       *    versus inflow).\n       */\n      template <typename Lambda>\n      state_type\n      apply_boundary_conditions(const dealii::types::boundary_id id,\n                                const state_type &U,\n                                const dealii::Tensor<1, dim, Number> &normal,\n                                const Lambda &get_dirichlet_data) const;\n\n      //@}\n      /**\n       * @name Flux computations\n       */\n      //@{\n\n      /**\n       * Given a state @p U and a pressure @p p compute the flux\n       * \\f[\n       * \\begin{pmatrix}\n       *   \\textbf m \\\\\n       *   \\textbf v\\otimes \\textbf m + p\\mathbb{I}_d \\\\\n       *   \\textbf v(E+p)\n       * \\end{pmatrix},\n       * \\f]\n       */\n      flux_type f(const state_type &U, const Number &p) const;\n\n      /**\n       * Given a state @p U_i and an index @p i compute flux contributions.\n       *\n       * Intended usage:\n       * ```\n       * Indicator<dim, Number> indicator;\n       * for (unsigned int i = n_internal; i < n_owned; ++i) {\n       *   // ...\n       *   const auto flux_i = flux_contribution(precomputed..., i, U_i);\n       *   for (unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {\n       *     // ...\n       *     const auto flux_j = flux_contribution(precomputed..., js, U_j);\n       *     const auto flux_ij = flux_divergence(flux_i, flux_j, c_ij);\n       *   }\n       * }\n       * ```\n       *\n       * For the Euler equations we simply compute <code>f(U_i)</code>.\n       */\n      flux_contribution_type\n      flux_contribution(const PrecomputedVector &pv,\n                        const InitialPrecomputedVector &piv,\n                        const unsigned int i,\n                        const state_type &U_i) const;\n\n      flux_contribution_type\n      flux_contribution(const PrecomputedVector &pv,\n                        const InitialPrecomputedVector &piv,\n                        const unsigned int *js,\n                        const state_type &U_j) const;\n\n      /**\n       * Given flux contributions @p flux_i and @p flux_j compute the flux\n       * <code>(-f(U_i) - f(U_j)</code>\n       */\n      state_type\n      flux_divergence(const flux_contribution_type &flux_i,\n                      const flux_contribution_type &flux_j,\n                      const dealii::Tensor<1, dim, Number> &c_ij) const;\n\n      /**\n       * The low-order and high-order fluxes are the same:\n       */\n      static constexpr bool have_high_order_flux = false;\n\n      state_type high_order_flux_divergence(\n          const flux_contribution_type &flux_i,\n          const flux_contribution_type &flux_j,\n          const dealii::Tensor<1, dim, Number> &c_ij) const = delete;\n\n      /**\n       * @name Computing stencil source terms\n       */\n      //@{\n\n      /** We do not have source terms */\n      static constexpr bool have_source_terms = false;\n\n      state_type nodal_source(const PrecomputedVector &pv,\n                              const unsigned int i,\n                              const state_type &U_i,\n                              const ScalarNumber tau) const = delete;\n\n      state_type nodal_source(const PrecomputedVector &pv,\n                              const unsigned int *js,\n                              const state_type &U_j,\n                              const ScalarNumber tau) const = delete;\n\n      //@}\n      /**\n       * @name State transformations\n       */\n      //@{\n\n      /**\n       * Given a state vector associated with a different spatial\n       * dimensions than the current one, return an \"expanded\" version of\n       * the state vector associated with @a dim spatial dimensions where\n       * the momentum vector of the conserved state @p state is expaned\n       * with zeros to a total length of @a dim entries.\n       *\n       * @note @a dim has to be larger or equal than the dimension of the\n       * @a ST vector.\n       */\n      template <typename ST>\n      state_type expand_state(const ST &state) const;\n\n      /**\n       * Given an initial state [rho, u_1, ..., u_d, p] return a\n       * conserved state [rho, m_1, ..., m_d, E]. Most notably, the\n       * specific equation of state oracle is queried to convert the\n       * pressure value into a specific internal energy.\n       *\n       * @note This function is used to conveniently convert (user\n       * provided) primitive initial states with pressure values to a\n       * conserved state in the EulerInitialStateLibrary. As such, this\n       * function is implemented in the Euler::HyperbolicSystem and\n       * EulerAEOS::HyperbolicSystem classes.\n       */\n      template <typename ST>\n      state_type from_initial_state(const ST &initial_state) const;\n\n      /**\n       * Given a primitive state [rho, u_1, ..., u_d, e] return a conserved\n       * state.\n       */\n      state_type from_primitive_state(const state_type &primitive_state) const;\n\n      /**\n       * Given a conserved state return a primitive state [rho, u_1, ..., u_d,\n       * e]\n       */\n      state_type to_primitive_state(const state_type &state) const;\n\n      /**\n       * Transform the current state according to a  given operator\n       * @p lambda acting on a @a dim dimensional momentum (or velocity)\n       * vector.\n       */\n      template <typename Lambda>\n      state_type apply_galilei_transform(const state_type &state,\n                                         const Lambda &lambda) const;\n      //@}\n    }; /* HyperbolicSystemView */\n\n\n    /*\n     * -------------------------------------------------------------------------\n     * Inline definitions\n     * -------------------------------------------------------------------------\n     */\n\n\n    inline HyperbolicSystem::HyperbolicSystem(\n        const std::string &subsection /*= \"HyperbolicSystem\"*/)\n        : ParameterAcceptor(subsection)\n    {\n      equation_of_state_ = \"polytropic gas\";\n      add_parameter(\n          \"equation of state\",\n          equation_of_state_,\n          \"The equation of state. Valid names are given by any of the \"\n          \"subsections defined below\");\n\n      compute_strict_bounds_ = true;\n      add_parameter(\n          \"compute strict bounds\",\n          compute_strict_bounds_,\n          \"Compute strict, but significantly more expensive bounds at various \"\n          \"places: (a) an expensive, but better upper wavespeed estimate in \"\n          \"the approximate RiemannSolver; (b) entropy viscosity-commutator \"\n          \"with correct gamma_min over the stencil; (c) mathematically correct \"\n          \"surrogate specific entropy minimum with gamma_min over the \"\n          \"stencil.\");\n\n      reference_density_ = 1.;\n      add_parameter(\"reference density\",\n                    reference_density_,\n                    \"Problem specific density reference\");\n\n      vacuum_state_relaxation_small_ = 1.e2;\n      add_parameter(\"vacuum state relaxation small\",\n                    vacuum_state_relaxation_small_,\n                    \"Problem specific vacuum relaxation parameter\");\n\n      vacuum_state_relaxation_large_ = 1.e4;\n      add_parameter(\"vacuum state relaxation large\",\n                    vacuum_state_relaxation_large_,\n                    \"Problem specific vacuum relaxation parameter\");\n\n      /*\n       * And finally populate the equation of state list with all equation of\n       * state configurations defined in the EquationOfState namespace:\n       */\n      EquationOfStateLibrary::populate_equation_of_state_list(\n          equation_of_state_list_, subsection);\n\n      const auto populate_functions = [this]() {\n        bool initialized = false;\n        for (auto &it : equation_of_state_list_)\n\n          /* Populate EOS-specific quantities and functions */\n          if (it->name() == equation_of_state_) {\n            selected_equation_of_state_ = it;\n            problem_name =\n                \"Compressible Euler equations (\" + it->name() + \" EOS)\";\n            initialized = true;\n            break;\n          }\n\n        AssertThrow(\n            initialized,\n            dealii::ExcMessage(\n                \"Could not find an equation of state description with name \\\"\" +\n                equation_of_state_ + \"\\\"\"));\n      };\n\n      ParameterAcceptor::parse_parameters_call_back.connect(populate_functions);\n      populate_functions();\n    }\n\n\n    template <int dim, typename ScalarNumber>\n    inline void HyperbolicSystem::fill_precomputed_values(\n        const OfflineData<dim, ScalarNumber> &offline_data,\n        typename HyperbolicSystemView<dim, ScalarNumber>::StateVector\n            &state_vector,\n        const bool skip_constrained_dofs) const\n    {\n      const unsigned int n_internal = offline_data.n_locally_internal();\n      const unsigned int n_owned = offline_data.n_locally_owned();\n      const auto &sparsity_simd = offline_data.sparsity_pattern_simd();\n      using VA = dealii::VectorizedArray<ScalarNumber>;\n\n      const auto &U = std::get<0>(state_vector);\n      auto &precomputed = std::get<1>(state_vector);\n\n      /* Compute values over the diagonal: */\n\n      const auto body = [&](auto sentinel, unsigned int i) {\n        using T = decltype(sentinel);\n        using View = HyperbolicSystemView<dim, T>;\n        using precomputed_type = typename View::precomputed_type;\n\n        const unsigned int row_length = sparsity_simd.row_length(i);\n        if (skip_constrained_dofs && row_length == 1)\n          return;\n\n        const auto U_i = U.template read_tensor<T>(i);\n        const auto view = this->view<dim, T>();\n        const auto rho_i = view.density(U_i);\n        const auto e_i = view.internal_energy(U_i) / rho_i;\n\n        /* Calls into the selected equation of state: */\n        const auto p_i = view.eos_pressure(rho_i, e_i);\n\n        const auto gamma_i = view.surrogate_gamma(U_i, p_i);\n        using PT = precomputed_type;\n        const PT prec_i{p_i, gamma_i, T(0.), T(0.)};\n        precomputed.template write_tensor<T>(prec_i, i);\n      };\n\n      cpu_simd_loop<ScalarNumber>(\"time_step_1\", body, 0, n_internal, n_owned);\n      precomputed.update_ghost_values();\n\n      /* Compute gamma_min over the stencil: */\n\n      const auto body_stencil = [&](auto sentinel, unsigned int i) {\n        using T = decltype(sentinel);\n        using View = HyperbolicSystemView<dim, T>;\n        using PT = typename View::precomputed_type;\n\n        const unsigned int row_length = sparsity_simd.row_length(i);\n        if (skip_constrained_dofs && row_length == 1)\n          return;\n\n        const auto U_i = U.template read_tensor<T>(i);\n        auto prec_i = precomputed.template read_tensor<T, PT>(i);\n        /* Previous loop: gamma_min_i == gamma_i, s_i == 0, eta_i == 0 */\n        auto &[p_i, gamma_min_i, s_i, eta_i] = prec_i;\n\n        const auto view = this->view<dim, T>();\n\n        constexpr unsigned int stride_size = get_stride_size<T>;\n        const unsigned int *js = sparsity_simd.columns(i) + stride_size;\n        for (unsigned int col_idx = 1; col_idx < row_length;\n             ++col_idx, js += stride_size) {\n\n          const auto U_j = U.template read_tensor<T>(js);\n          const auto prec_j = precomputed.template read_tensor<T, PT>(js);\n          const auto p_j = std::get<0>(prec_j);\n          const auto gamma_j = view.surrogate_gamma(U_j, p_j);\n          gamma_min_i = std::min(gamma_min_i, gamma_j);\n        }\n\n        s_i = view.surrogate_specific_entropy(U_i, gamma_min_i);\n        eta_i = view.surrogate_harten_entropy(U_i, gamma_min_i);\n        precomputed.template write_tensor<T>(prec_i, i);\n      };\n\n      cpu_simd_loop<ScalarNumber>(\n          \"time_step_1\", body_stencil, 0, n_internal, n_owned);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::density(const state_type &U)\n    {\n      return U[0];\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::filter_vacuum_density(\n        const Number &rho) const\n    {\n      constexpr ScalarNumber eps = std::numeric_limits<ScalarNumber>::epsilon();\n      const Number rho_cutoff_large =\n          reference_density() * vacuum_state_relaxation_large() * eps;\n\n      return dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(\n          std::abs(rho), rho_cutoff_large, Number(0.), rho);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline dealii::Tensor<1, dim, Number>\n    HyperbolicSystemView<dim, Number>::momentum(const state_type &U)\n    {\n      dealii::Tensor<1, dim, Number> result;\n      for (unsigned int i = 0; i < dim; ++i)\n        result[i] = U[1 + i];\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::total_energy(const state_type &U)\n    {\n      return U[1 + dim];\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::internal_energy(const state_type &U)\n    {\n      /*\n       * rho e = (E - 1/2*m^2/rho)\n       */\n      const Number rho_inverse = ScalarNumber(1.) / density(U);\n      const auto m = momentum(U);\n      const Number E = total_energy(U);\n      return E - ScalarNumber(0.5) * m.norm_square() * rho_inverse;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::internal_energy_derivative(\n        const state_type &U) -> state_type\n    {\n      /*\n       * With\n       *   rho e = E - 1/2 |m|^2 / rho\n       * we get\n       *   (rho e)' = (1/2m^2/rho^2, -m/rho , 1 )^T\n       */\n\n      const Number rho_inverse = ScalarNumber(1.) / density(U);\n      const auto u = momentum(U) * rho_inverse;\n\n      state_type result;\n\n      result[0] = ScalarNumber(0.5) * u.norm_square();\n      for (unsigned int i = 0; i < dim; ++i) {\n        result[1 + i] = -u[i];\n      }\n      result[dim + 1] = ScalarNumber(1.);\n\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::surrogate_specific_entropy(\n        const state_type &U, const Number &gamma_min) const\n    {\n      const auto b = Number(eos_interpolation_b());\n      const auto pinf = Number(eos_interpolation_pinfty());\n      const auto q = Number(eos_interpolation_q());\n\n      const auto rho = density(U);\n      const auto rho_inverse = ScalarNumber(1.) / rho;\n\n      const auto covolume = Number(1.) - b * rho;\n\n      const auto shift = internal_energy(U) - rho * q - pinf * covolume;\n\n      return shift * ryujin::pow(rho_inverse - b, gamma_min) / covolume;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::surrogate_harten_entropy(\n        const state_type &U, const Number &gamma_min) const\n    {\n      const auto b = Number(eos_interpolation_b());\n      const auto pinf = Number(eos_interpolation_pinfty());\n      const auto q = Number(eos_interpolation_q());\n\n      const auto rho = density(U);\n      const auto m = momentum(U);\n      const auto E = total_energy(U);\n      const auto rho_rho_e_q =\n          rho * E - ScalarNumber(0.5) * m.norm_square() - rho * rho * q;\n\n      const auto exponent = ScalarNumber(1.) / (gamma_min + Number(1.));\n\n      const auto covolume = Number(1.) - b * rho;\n      const auto covolume_term = ryujin::pow(covolume, gamma_min - Number(1.));\n\n      const auto rho_pinfcov = rho * pinf * covolume;\n\n      return ryujin::pow(\n          positive_part(rho_rho_e_q - rho_pinfcov) * covolume_term, exponent);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::surrogate_harten_entropy_derivative(\n        const state_type &U, const Number &eta, const Number &gamma_min) const\n        -> state_type\n    {\n      /*\n       * With\n       *   eta = (shift * (1-b*rho)^{gamma-1}) ^ {1/(gamma+1)},\n       *   shift = rho * E - 1/2 |m|^2 - rho^2 * q - p_infty * rho * (1 - b rho)\n       *\n       *   shift' = [E - 2 * rho * q - p_infty * (1 - 2 b rho), -m, rho]^T\n       *   factor = 1/(gamma+1) * (eta/(1-b rho))^-gamma / (1-b rho)^2\n       *\n       * we get\n       *\n       *   eta' = factor * (1-b*rho) * shift' -\n       *          factor * shift * (gamma - 1) * b * [1, 0, 0]^T\n       *\n       */\n      const auto b = Number(eos_interpolation_b());\n      const auto pinf = Number(eos_interpolation_pinfty());\n      const auto q = Number(eos_interpolation_q());\n\n      const auto rho = density(U);\n      const auto m = momentum(U);\n      const auto E = total_energy(U);\n\n      const auto covolume = Number(1.) - b * rho;\n      const auto covolume_inverse = ScalarNumber(1.) / covolume;\n\n      const auto shift = rho * E - ScalarNumber(0.5) * m.norm_square() -\n                         rho * rho * q - rho * pinf * covolume;\n\n      constexpr auto eps = std::numeric_limits<ScalarNumber>::epsilon();\n      const auto regularization = m.norm() * eps;\n      const auto max_val = ryujin::pow(\n          std::max(regularization, eta * covolume_inverse), gamma_min);\n      auto factor = safe_division(Number(1.0), max_val);\n      factor *= fixed_power<2>(covolume_inverse) / (gamma_min + Number(1.));\n\n      state_type result;\n\n      const auto first_term = E - ScalarNumber(2.) * rho * q -\n                              pinf * (Number(1.) - ScalarNumber(2.) * b * rho);\n      const auto second_term = -(gamma_min - Number(1.)) * shift * b;\n\n      result[0] = factor * (covolume * first_term + second_term);\n      for (unsigned int i = 0; i < dim; ++i)\n        result[1 + i] = -factor * covolume * m[i];\n      result[dim + 1] = factor * covolume * rho;\n\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::surrogate_gamma(const state_type &U,\n                                                       const Number &p) const\n    {\n      const auto b = Number(eos_interpolation_b());\n      const auto pinf = Number(eos_interpolation_pinfty());\n      const auto q = Number(eos_interpolation_q());\n\n      const auto rho = density(U);\n      const auto rho_e = internal_energy(U);\n      const auto covolume = Number(1.) - b * rho;\n\n      const auto numerator = (p + pinf) * covolume;\n      const auto denominator = rho_e - rho * q - covolume * pinf;\n      return Number(1.) + safe_division(numerator, denominator);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::surrogate_pressure(\n        const state_type &U, const Number &gamma) const\n    {\n      const auto b = Number(eos_interpolation_b());\n      const auto pinf = Number(eos_interpolation_pinfty());\n      const auto q = Number(eos_interpolation_q());\n\n      const auto rho = density(U);\n      const auto rho_e = internal_energy(U);\n      const auto covolume = Number(1.) - b * rho;\n\n      return positive_part(gamma - Number(1.)) *\n                 safe_division(rho_e - rho * q, covolume) -\n             gamma * pinf;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::surrogate_speed_of_sound(\n        const state_type &U, const Number &gamma) const\n    {\n      const auto b = Number(eos_interpolation_b());\n      const auto pinf = Number(eos_interpolation_pinfty());\n      const auto q = Number(eos_interpolation_q());\n\n      const auto rho = density(U);\n      const auto rho_e = internal_energy(U);\n      const auto covolume = Number(1.) - b * rho;\n\n      auto radicand =\n          (rho_e - rho * q - pinf * covolume) / (covolume * covolume * rho);\n      radicand *= gamma * (gamma - 1.);\n      return std::sqrt(positive_part(radicand));\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline bool\n    HyperbolicSystemView<dim, Number>::is_admissible(const state_type &U) const\n    {\n      const auto b = Number(eos_interpolation_b());\n      const auto pinf = Number(eos_interpolation_pinfty());\n      const auto q = Number(eos_interpolation_q());\n\n      const auto rho = density(U);\n      const auto rho_e = internal_energy(U);\n      const auto covolume = Number(1.) - b * rho;\n\n      const auto shift = rho_e - rho * q - pinf * covolume;\n\n      constexpr auto gt = dealii::SIMDComparison::greater_than;\n      using T = Number;\n      const auto test =\n          dealii::compare_and_apply_mask<gt>(rho, T(0.), T(0.), T(-1.)) + //\n          dealii::compare_and_apply_mask<gt>(shift, T(0.), T(0.), T(-1.));\n\n#ifdef DEBUG_OUTPUT\n      if (!(test == Number(0.))) {\n        std::cout << std::fixed << std::setprecision(16);\n        std::cout << \"Bounds violation: Negative state [rho, e] detected!\\n\";\n        std::cout << \"\\t\\trho:           \" << rho << \"\\n\";\n        std::cout << \"\\t\\tint (shifted): \" << shift << \"\\n\";\n      }\n#endif\n\n      return (test == Number(0.));\n    }\n\n\n    template <int dim, typename Number>\n    template <int component>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::prescribe_riemann_characteristic(\n        const state_type &U,\n        const Number &p,\n        const state_type &U_bar,\n        const Number &p_bar,\n        const dealii::Tensor<1, dim, Number> &normal) const -> state_type\n    {\n      static_assert(component == 1 || component == 2,\n                    \"component has to be 1 or 2\");\n\n      const auto b = Number(eos_interpolation_b());\n      const auto pinf = Number(eos_interpolation_pinfty());\n      const auto q = Number(eos_interpolation_q());\n\n      /*\n       * The \"four\" Riemann characteristics are formed under the assumption\n       * of a locally isentropic flow. For this, we first transform both\n       * states into {rho, vn, vperp, gamma, a}, where we use the NASG EOS\n       * interpolation to derive a surrogate gamma and speed of sound a.\n       *\n       * See, e.g., https://arxiv.org/pdf/2004.08750, \"Compressible flow in\n       * a NOble-Abel Stiffened-Gas fluid\", M. I. Radulescu.\n       */\n\n      const auto m = momentum(U);\n      const auto rho = density(U);\n      const auto vn = m * normal / rho;\n\n      const auto gamma = surrogate_gamma(U, p);\n      const auto a = surrogate_speed_of_sound(U, gamma);\n      const auto covolume = 1. - b * rho;\n\n      const auto m_bar = momentum(U_bar);\n      const auto rho_bar = density(U_bar);\n      const auto vn_bar = m_bar * normal / rho_bar;\n\n      const auto gamma_bar = surrogate_gamma(U_bar, p_bar);\n      const auto a_bar = surrogate_speed_of_sound(U_bar, gamma_bar);\n      const auto covolume_bar = 1. - b * rho_bar;\n\n      /*\n       * Now compute the Riemann characteristics {R_1, R_2, vperp, s}:\n       *   R_1 = v * n - 2 / (gamma - 1) * a * (1 - b * rho)\n       *   R_2 = v * n + 2 / (gamma - 1) * a * (1 - b * rho)\n       *   vperp\n       *   S = (p + p_infty) / rho^gamma * (1 - b * rho)^gamma\n       *\n       * Here, we replace either R_1, or R_2 with values coming from U_bar:\n       */\n\n      const auto R_1 =\n          component == 1 ? vn_bar - 2. * a_bar / (gamma_bar - 1.) * covolume_bar\n                         : vn - 2. * a / (gamma - 1.) * covolume;\n\n      const auto R_2 =\n          component == 2 ? vn_bar + 2. * a_bar / (gamma_bar - 1.) * covolume_bar\n                         : vn + 2. * a / (gamma - 1.) * covolume;\n\n      /*\n       * Note that we are really hoping for the best here... We require\n       * that R_2 >= R_1 so that we can extract a valid sound speed...\n       */\n\n      Assert(\n          R_2 >= R_1,\n          dealii::ExcMessage(\"Encountered R_2 < R_1 in dynamic boundary value \"\n                             \"enforcement. This implies that the interpolation \"\n                             \"with Riemann characteristics failed.\"));\n\n      const auto vperp = m / rho - vn * normal;\n\n      const auto S = (p + pinf) * ryujin::pow(Number(1.) / rho - b, gamma);\n\n      /*\n       * Now, we have to reconstruct the actual conserved state U from the\n       * Riemann characteristics R_1, R_2, vperp, and s. We first set up\n       * {vn_new, vperp_new, a_new, S} and then solve for {rho_new, p_new}\n       * with the help of the NASG EOS surrogate formulas:\n       *\n       *   S = (p + p_infty) / rho^gamma * (1 - b * rho)^gamma\n       *\n       *   a^2 = gamma * (p + p_infty) / (rho * cov)\n       *\n       *   This implies:\n       *\n       *   a^2 / (gamma * S) = rho^{gamma - 1} / (1 - b * rho)^{1 + gamma}\n       */\n\n      const auto vn_new = Number(0.5) * (R_1 + R_2);\n\n      /*\n       * Technically, we would need to solve for rho subject to a number of\n       * nonlinear relationships:\n       *\n       *   a   = (gamma - 1) * (R_2 - R_1) / (4. * (1 - b * rho))\n       *\n       *   a^2 / (gamma * S) = rho^{gamma - 1} / (1 - b * rho)^{gamma + 1}\n       *\n       * This seems to be a bit expensive for the fact that our dynamic\n       * boundary conditions are already terribly heuristic...\n       *\n       * So instead, we rewrite this system as:\n       *\n       *   a * (1 - b * rho) = (gamma - 1) * (R_2 - R_1) / 4.\n       *\n       *   a^2 / (gamma * S) (1 - b * rho)^2\n       *                           = (rho / (1 - b * rho))^{gamma - 1}\n       *\n       * And compute the terms on the left simply with the old covolume and\n       * solving an easier easier nonlinear equation for the density. The\n       * resulting system reads:\n       *\n       *   a = (gamma - 1) * (R_2 - R_1) / (4. * (1 - b * rho_old))\n       *   A = {a^2 / (gamma * S) (1 - b * rho_old)^{2 gamma}}^{1/(gamma - 1)}\n       *\n       *   rho = A / (1 + b * A)\n       */\n\n      const auto a_new_square =\n          ryujin::fixed_power<2>((gamma - 1.) * (R_2 - R_1) / (4. * covolume));\n\n      auto term = ryujin::pow(a_new_square / (gamma * S), 1. / (gamma - 1.));\n      if (b != ScalarNumber(0.)) {\n        term *= std::pow(covolume, 2. / (gamma - 1.));\n      }\n\n      const auto rho_new = term / (1. + b * term);\n\n      const auto covolume_new = (1. - b * rho_new);\n      const auto p_new = a_new_square / gamma * rho_new * covolume_new - pinf;\n\n      /*\n       * And translate back into conserved quantities:\n       */\n\n      const auto rho_e_new =\n          rho_new * q + (p_new + gamma * pinf) * covolume_new / (gamma - 1.);\n\n      state_type U_new;\n      U_new[0] = rho_new;\n      for (unsigned int d = 0; d < dim; ++d) {\n        U_new[1 + d] = rho_new * (vn_new * normal + vperp)[d];\n      }\n      U_new[1 + dim] =\n          rho_e_new + 0.5 * rho_new * (vn_new * vn_new + vperp.norm_square());\n\n      return U_new;\n    }\n\n\n    template <int dim, typename Number>\n    template <typename Lambda>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::apply_boundary_conditions(\n        dealii::types::boundary_id id,\n        const state_type &U,\n        const dealii::Tensor<1, dim, Number> &normal,\n        const Lambda &get_dirichlet_data) const -> state_type\n    {\n      state_type result = U;\n\n      if (id == Boundary::dirichlet) {\n        result = get_dirichlet_data();\n\n      } else if (id == Boundary::dirichlet_momentum) {\n        /*\n         * Only enforce Dirichlet conditions on the momentum, and keep the\n         * internal energy constant:\n         */\n        const auto m_dirichlet = momentum(get_dirichlet_data());\n        const auto rho = density(result);\n        const auto m = momentum(result);\n\n        for (unsigned int k = 0; k < dim; ++k)\n          result[k + 1] = m_dirichlet[k];\n        result[dim + 1] +=\n            Number(0.5) / rho * (m_dirichlet.norm_square() - m.norm_square());\n\n      } else if (id == Boundary::dirichlet_velocity) {\n        /*\n         * Only enforce Dirichlet conditions on the velocity, and keep the\n         * internal energy constant:\n         */\n        const auto U_dirichlet = get_dirichlet_data();\n        const auto rho_dirichlet = density(U_dirichlet);\n        const auto v_dirichlet = momentum(U_dirichlet) / rho_dirichlet;\n        const auto rho = density(result);\n        const auto v = momentum(result) / rho;\n\n        for (unsigned int k = 0; k < dim; ++k)\n          result[k + 1] = rho * v_dirichlet[k];\n        result[dim + 1] +=\n            Number(0.5) * rho * (v_dirichlet.norm_square() - v.norm_square());\n\n      } else if (id == Boundary::slip) {\n        auto m = momentum(U);\n        m -= 1. * (m * normal) * normal;\n        for (unsigned int k = 0; k < dim; ++k)\n          result[k + 1] = m[k];\n\n      } else if (id == Boundary::no_slip) {\n        for (unsigned int k = 0; k < dim; ++k)\n          result[k + 1] = Number(0.);\n\n      } else if (id == Boundary::dynamic) {\n        /*\n         * On dynamic boundary conditions, we distinguish four cases:\n         *\n         *  - supersonic inflow: prescribe full state\n         *  - subsonic inflow:\n         *      decompose into Riemann invariants and leave R_2\n         *      characteristic untouched.\n         *  - supersonic outflow: do nothing\n         *  - subsonic outflow:\n         *      decompose into Riemann invariants and prescribe incoming\n         *      R_1 characteristic.\n         */\n        const auto m = momentum(U);\n        const auto rho = density(U);\n        const auto rho_e = internal_energy(U);\n\n        /*\n         * We do not have precomputed values available. Thus, simply query\n         * the pressure oracle and compute a surrogate speed of sound from\n         * there:\n         */\n        const auto p = eos_pressure(rho, rho_e / rho);\n        const auto gamma = surrogate_gamma(U, p);\n        const auto a = surrogate_speed_of_sound(U, gamma);\n        const auto vn = m * normal / rho;\n\n        /* Supersonic inflow: */\n        if (vn < -a) {\n          result = get_dirichlet_data();\n        }\n\n        /* Subsonic inflow: */\n        if (vn >= -a && vn <= 0.) {\n          const auto U_dirichlet = get_dirichlet_data();\n          const auto rho_dirichlet = density(U_dirichlet);\n          const auto rho_e_dirichlet = internal_energy(U_dirichlet);\n          const auto p_dirichlet =\n              eos_pressure(rho_dirichlet, rho_e_dirichlet / rho_dirichlet);\n\n          result = prescribe_riemann_characteristic<2>(\n              U_dirichlet, p_dirichlet, U, p, normal);\n        }\n\n        /* Subsonic outflow: */\n        if (vn > 0. && vn <= a) {\n          const auto U_dirichlet = get_dirichlet_data();\n          const auto rho_dirichlet = density(U_dirichlet);\n          const auto rho_e_dirichlet = internal_energy(U_dirichlet);\n          const auto p_dirichlet =\n              eos_pressure(rho_dirichlet, rho_e_dirichlet / rho_dirichlet);\n\n          result = prescribe_riemann_characteristic<1>(\n              U, p, U_dirichlet, p_dirichlet, normal);\n        }\n        /* Supersonic outflow: do nothing, i.e., keep U as is */\n\n      } else {\n        AssertThrow(false, dealii::ExcNotImplemented());\n      }\n\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::f(const state_type &U,\n                                         const Number &p) const -> flux_type\n    {\n      const auto rho_inverse = ScalarNumber(1.) / density(U);\n      const auto m = momentum(U);\n      const auto E = total_energy(U);\n\n      flux_type result;\n\n      result[0] = m;\n      for (unsigned int i = 0; i < dim; ++i) {\n        result[1 + i] = m * (m[i] * rho_inverse);\n        result[1 + i][i] += p;\n      }\n      result[dim + 1] = m * (rho_inverse * (E + p));\n\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::flux_contribution(\n        const PrecomputedVector &pv,\n        const InitialPrecomputedVector & /*piv*/,\n        const unsigned int i,\n        const state_type &U_i) const -> flux_contribution_type\n    {\n      const auto &[p_i, gamma_min_i, s_i, eta_i] =\n          pv.template read_tensor<Number, precomputed_type>(i);\n      return f(U_i, p_i);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::flux_contribution(\n        const PrecomputedVector &pv,\n        const InitialPrecomputedVector & /*piv*/,\n        const unsigned int *js,\n        const state_type &U_j) const -> flux_contribution_type\n    {\n      const auto &[p_j, gamma_min_j, s_j, eta_j] =\n          pv.template read_tensor<Number, precomputed_type>(js);\n      return f(U_j, p_j);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::flux_divergence(\n        const flux_contribution_type &flux_i,\n        const flux_contribution_type &flux_j,\n        const dealii::Tensor<1, dim, Number> &c_ij) const -> state_type\n    {\n      return -contract(add(flux_i, flux_j), c_ij);\n    }\n\n\n    template <int dim, typename Number>\n    template <typename ST>\n    auto HyperbolicSystemView<dim, Number>::expand_state(const ST &state) const\n        -> state_type\n    {\n      using T = typename ST::value_type;\n      static_assert(std::is_same_v<Number, T>, \"template mismatch\");\n\n      constexpr auto dim2 = ST::dimension - 2;\n      static_assert(dim >= dim2,\n                    \"the space dimension of the argument state must not be \"\n                    \"larger than the one of the target state\");\n\n      state_type result;\n      result[0] = state[0];\n      result[dim + 1] = state[dim2 + 1];\n      for (unsigned int i = 1; i < dim2 + 1; ++i)\n        result[i] = state[i];\n\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    template <typename ST>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::from_initial_state(\n        const ST &initial_state) const -> state_type\n    {\n      auto primitive_state = expand_state(initial_state);\n\n      /* pressure into specific internal energy: */\n      const auto rho = density(primitive_state);\n      const auto p = /*SIC!*/ total_energy(primitive_state);\n      const auto e = eos_specific_internal_energy(rho, p);\n      primitive_state[dim + 1] = e;\n\n      return from_primitive_state(primitive_state);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::from_primitive_state(\n        const state_type &primitive_state) const -> state_type\n    {\n      const auto rho = density(primitive_state);\n      /* extract velocity: */\n      const auto u = /*SIC!*/ momentum(primitive_state);\n      /* extract specific internal energy: */\n      const auto &e = /*SIC!*/ total_energy(primitive_state);\n\n      auto state = primitive_state;\n      /* Fix up momentum: */\n      for (unsigned int i = 1; i < dim + 1; ++i)\n        state[i] *= rho;\n\n      /* Compute total energy: */\n      state[dim + 1] = rho * e + Number(0.5) * rho * u * u;\n\n      return state;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::to_primitive_state(\n        const state_type &state) const -> state_type\n    {\n      const auto rho = density(state);\n      const auto rho_inverse = Number(1.) / rho;\n      const auto rho_e = internal_energy(state);\n\n      auto primitive_state = state;\n      /* Fix up velocity: */\n      for (unsigned int i = 1; i < dim + 1; ++i)\n        primitive_state[i] *= rho_inverse;\n      /* Set specific internal energy: */\n      primitive_state[dim + 1] = rho_e * rho_inverse;\n\n      return primitive_state;\n    }\n\n\n    template <int dim, typename Number>\n    template <typename Lambda>\n    auto HyperbolicSystemView<dim, Number>::apply_galilei_transform(\n        const state_type &state, const Lambda &lambda) const -> state_type\n    {\n      auto result = state;\n      const auto M = lambda(momentum(state));\n      for (unsigned int d = 0; d < dim; ++d)\n        result[1 + d] = M[d];\n      return result;\n    }\n  } // namespace EulerAEOS\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_aeos/indicator.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"hyperbolic_system.h\"\n\n#include <multicomponent_vector.h>\n#include <simd.h>\n\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/vectorization.h>\n\n\nnamespace ryujin\n{\n  namespace EulerAEOS\n  {\n    template <typename ScalarNumber = double>\n    class IndicatorParameters : public dealii::ParameterAcceptor\n    {\n    public:\n      IndicatorParameters(const std::string &subsection = \"/Indicator\")\n          : ParameterAcceptor(subsection)\n      {\n        evc_factor_ = ScalarNumber(1.);\n        add_parameter(\"evc factor\",\n                      evc_factor_,\n                      \"Factor for scaling the entropy viscocity commuator\");\n      }\n\n      ACCESSOR_READ_ONLY(evc_factor);\n\n    private:\n      ScalarNumber evc_factor_;\n    };\n\n\n    /**\n     * This class implements an indicator strategy used to form the\n     * preliminary high-order update.\n     *\n     * The indicator is an entropy-viscosity commutator as described\n     * in @cite GuermondEtAl2011 and @cite GuermondEtAl2018. For a given\n     * entropy \\f$\\eta\\f$ (either the mathematical entropy, or a Harten\n     * entropy, see the documentation of HyperbolicSystem) we let\n     * \\f$\\eta'\\f$ denote its derivative with respect to the state variables.\n     * We then compute a normalized entropy viscosity ratio \\f$\\alpha_i^n\\f$\n     * for the state \\f$\\boldsymbol U_i^n\\f$ as follows:\n     * \\f{align}\n     *   \\alpha_i^n\\;=\\;\\frac{N_i^n}{D_i^n},\n     *   \\quad\n     *   N_i^n\\;:=\\;\\left|a_i^n- \\eta'(\\boldsymbol U^n_i)\\cdot\\boldsymbol\n     *   b_i^n +\\frac{\\eta(\\boldsymbol U^n_i)}{\\rho_i^n}\\big(\\boldsymbol\n     *   b_i^n\\big)_1\\right|,\n     *   \\quad\n     *   D_i^n\\;:=\\;\\left|a_i^n\\right| +\n     *   \\sum_{k=1}^{d+1}\\left|\\big(\\eta'(\\boldsymbol U^n_i)\\big)_k-\n     *   \\delta_{1k}\\frac{\\eta(\\boldsymbol U^n_i)}{\\rho_i^n}\\right|\n     *   \\,\\left|\\big(\\boldsymbol b_i^n\\big)_k\\right|,\n     * \\f}\n     * where where \\f$\\big(\\,.\\,\\big)_k\\f$ denotes the \\f$k\\f$-th component\n     * of a vector, \\f$\\delta_{ij}\\f$ is Kronecker's delta, and where we have\n     * set\n     * \\f{align}\n     *   a_i^n \\;:=\\;\n     *   \\sum_{j\\in\\mathcal{I}_i}\\left(\\frac{\\eta(\\boldsymbol U_j^n)}{\\rho_j^n}\n     *   -\\frac{\\eta(\\boldsymbol U_i^n)}{\\rho_i^n}\\right)\\,\n     *   \\boldsymbol m_j^n\\cdot\\boldsymbol c_{ij},\n     *   \\qquad\n     *   \\boldsymbol b_i^n \\;:=\\;\n     *   \\sum_{j\\in\\mathcal{I}_i}\\left(\\mathbf{f}(\\boldsymbol U_j^n)-\n     *   \\mathbf{f}(\\boldsymbol U_i^n)\\right)\\cdot\\boldsymbol c_{ij},\n     * \\f}\n     *\n     * @ingroup EulerEquations\n     */\n    template <int dim, typename Number = double>\n    class Indicator\n    {\n    public:\n      /**\n       * @name Typedefs and constexpr constants\n       */\n      //@{\n\n      using View = HyperbolicSystemView<dim, Number>;\n\n      using ScalarNumber = typename View::ScalarNumber;\n\n      static constexpr auto problem_dimension = View::problem_dimension;\n\n      using state_type = typename View::state_type;\n\n      using flux_type = typename View::flux_type;\n\n      using precomputed_type = typename View::precomputed_type;\n\n      using PrecomputedVector = typename View::PrecomputedVector;\n\n      using Parameters = IndicatorParameters<ScalarNumber>;\n\n      //@}\n      /**\n       * @name Stencil-based computation of indicators\n       *\n       * Intended usage:\n       * ```\n       * Indicator<dim, Number> indicator;\n       * for (unsigned int i = n_internal; i < n_owned; ++i) {\n       *   // ...\n       *   indicator.reset(i, U_i);\n       *   for (unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {\n       *     // ...\n       *     indicator.accumulate(js, U_j, c_ij);\n       *   }\n       *   indicator.alpha(hd_i);\n       * }\n       * ```\n       */\n      //@{\n\n      /**\n       * Constructor taking a HyperbolicSystem instance as argument\n       */\n      Indicator(const HyperbolicSystem &hyperbolic_system,\n                const Parameters &parameters,\n                const PrecomputedVector &precomputed_values)\n          : hyperbolic_system(hyperbolic_system)\n          , parameters(parameters)\n          , precomputed_values(precomputed_values)\n      {\n      }\n\n      /**\n       * Reset temporary storage and initialize for a new row corresponding\n       * to state vector U_i.\n       */\n      void reset(const unsigned int i, const state_type &U_i);\n\n      /**\n       * When looping over the sparsity row, add the contribution associated\n       * with the neighboring state U_j.\n       */\n      void accumulate(const unsigned int *js,\n                      const state_type &U_j,\n                      const dealii::Tensor<1, dim, Number> &c_ij);\n\n      /**\n       * Return the computed alpha_i value.\n       */\n      Number alpha(const Number h_i) const;\n\n      //@}\n\n    private:\n      /**\n       * @name\n       */\n      //@{\n\n      const HyperbolicSystem &hyperbolic_system;\n      const Parameters &parameters;\n      const PrecomputedVector &precomputed_values;\n\n      Number rho_i_inverse = 0.;\n      Number eta_i = 0.;\n      flux_type f_i;\n      state_type d_eta_i;\n      Number gamma_min;\n\n      Number left = 0.;\n      state_type right;\n\n      //@}\n    };\n\n\n    /*\n     * -------------------------------------------------------------------------\n     * Inline definitions\n     * -------------------------------------------------------------------------\n     */\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline void\n    Indicator<dim, Number>::reset(const unsigned int i, const state_type &U_i)\n    {\n      /* Entropy viscosity commutator: */\n\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      const auto &[p_i, gamma_min_i, s_i, new_eta_i] =\n          precomputed_values.template read_tensor<Number, precomputed_type>(i);\n\n      gamma_min = gamma_min_i;\n\n      const auto rho_i = view.density(U_i);\n      rho_i_inverse = Number(1.) / rho_i;\n      eta_i = new_eta_i;\n\n      d_eta_i = view.surrogate_harten_entropy_derivative(U_i, eta_i, gamma_min);\n      d_eta_i[0] -= eta_i * rho_i_inverse;\n\n      const auto surrogate_p_i = view.surrogate_pressure(U_i, gamma_min);\n      f_i = view.f(U_i, surrogate_p_i);\n\n      left = 0.;\n      right = 0.;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline void Indicator<dim, Number>::accumulate(\n        const unsigned int * /*js*/,\n        const state_type &U_j,\n        const dealii::Tensor<1, dim, Number> &c_ij)\n    {\n      /* Entropy viscosity commutator: */\n\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      const auto eta_j = view.surrogate_harten_entropy(U_j, gamma_min);\n\n      const auto rho_j = view.density(U_j);\n      const auto rho_j_inverse = Number(1.) / rho_j;\n\n      const auto m_j = view.momentum(U_j);\n\n      const auto surrogate_p_j = view.surrogate_pressure(U_j, gamma_min);\n      const auto f_j = view.f(U_j, surrogate_p_j);\n\n      const auto entropy_flux =\n          (eta_j * rho_j_inverse - eta_i * rho_i_inverse) * (m_j * c_ij);\n\n      left += entropy_flux;\n      for (unsigned int k = 0; k < problem_dimension; ++k) {\n        const auto component = (f_j[k] - f_i[k]) * c_ij;\n        right[k] += component;\n      }\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    Indicator<dim, Number>::alpha(const Number hd_i) const\n    {\n      /* Entropy viscosity commutator: */\n\n      Number numerator = left;\n      Number denominator = std::abs(left);\n      for (unsigned int k = 0; k < problem_dimension; ++k) {\n        numerator -= d_eta_i[k] * right[k];\n        denominator += std::abs(d_eta_i[k] * right[k]);\n      }\n\n      const auto quotient = safe_division(std::abs(numerator),\n                                          denominator + hd_i * std::abs(eta_i));\n\n      return std::min(Number(1.), parameters.evc_factor() * quotient);\n    }\n  } // namespace EulerAEOS\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_aeos/initial_state_library.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 by the ryujin authors\n//\n\n#include \"initial_state_library.template.h\"\n\nnamespace ryujin\n{\n  template class InitialStateLibrary<Description, 1, NUMBER>;\n  template class InitialStateLibrary<Description, 2, NUMBER>;\n  template class InitialStateLibrary<Description, 3, NUMBER>;\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_aeos/initial_state_library.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include \"description.h\"\n\n#include \"../euler/initial_state_library_euler.h\"\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  using Description = EulerAEOS::Description;\n\n  template <int dim, typename Number>\n  class InitialStateLibrary<Description, dim, Number>\n  {\n  public:\n    using HyperbolicSystem = typename Description::HyperbolicSystem;\n    using ParabolicSystem = typename Description::ParabolicSystem;\n\n    using View =\n        typename Description::template HyperbolicSystemView<dim, Number>;\n\n    using initial_state_list_type =\n        std::set<std::unique_ptr<InitialState<Description, dim, Number>>>;\n\n    static void\n    populate_initial_state_list(initial_state_list_type &initial_state_list,\n                                const HyperbolicSystem &h,\n                                const ParabolicSystem & /*p*/,\n                                const std::string &s)\n    {\n      EulerInitialStates::populate_initial_state_list<Description, dim, Number>(\n          initial_state_list, h, s);\n    }\n  };\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_aeos/instantiate.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 by the ryujin authors\n//\n\n#ifndef RYUJIN_INCLUDE_INSTANTIATION_ONCE\n#define RYUJIN_INCLUDE_INSTANTIATION_ONCE\n#else\n#error Instantiation files can only be included once.\n#endif\n\n#include \"description.h\"\n\nnamespace ryujin\n{\n  using EulerAEOS::Description;\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_aeos/limiter.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2023 by the ryujin authors\n//\n\n#include \"limiter.template.h\"\n\nusing namespace dealii;\n\nnamespace ryujin\n{\n  namespace EulerAEOS\n  {\n    /* instantiations */\n\n    template class Limiter<1, NUMBER>;\n    template class Limiter<2, NUMBER>;\n    template class Limiter<3, NUMBER>;\n\n    template class Limiter<1, dealii::VectorizedArray<NUMBER>>;\n    template class Limiter<2, dealii::VectorizedArray<NUMBER>>;\n    template class Limiter<3, dealii::VectorizedArray<NUMBER>>;\n  } // namespace EulerAEOS\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_aeos/limiter.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"hyperbolic_system.h\"\n\n#include <multicomponent_vector.h>\n#include <newton.h>\n#include <simd.h>\n\nnamespace ryujin\n{\n  namespace EulerAEOS\n  {\n    template <typename ScalarNumber = double>\n    class LimiterParameters : public dealii::ParameterAcceptor\n    {\n    public:\n      LimiterParameters(const std::string &subsection = \"/Limiter\")\n          : ParameterAcceptor(subsection)\n      {\n        iterations_ = 2;\n        add_parameter(\n            \"iterations\", iterations_, \"Number of limiter iterations\");\n\n        if constexpr (std::is_same_v<ScalarNumber, double>)\n          newton_tolerance_ = 1.e-10;\n        else\n          newton_tolerance_ = 1.e-4;\n        add_parameter(\"newton tolerance\",\n                      newton_tolerance_,\n                      \"Tolerance for the quadratic newton stopping criterion\");\n\n        newton_max_iterations_ = 2;\n        add_parameter(\"newton max iterations\",\n                      newton_max_iterations_,\n                      \"Maximal number of quadratic newton iterations performed \"\n                      \"during limiting\");\n\n        relaxation_factor_ = ScalarNumber(1.);\n        add_parameter(\"relaxation factor\",\n                      relaxation_factor_,\n                      \"Factor for scaling the relaxation window with r_i = \"\n                      \"factor * (m_i/|Omega|)^(1.5/d).\");\n      }\n\n      ACCESSOR_READ_ONLY(iterations);\n      ACCESSOR_READ_ONLY(newton_tolerance);\n      ACCESSOR_READ_ONLY(newton_max_iterations);\n      ACCESSOR_READ_ONLY(relaxation_factor);\n\n    private:\n      unsigned int iterations_;\n      ScalarNumber newton_tolerance_;\n      unsigned int newton_max_iterations_;\n      ScalarNumber relaxation_factor_;\n    };\n\n\n    /**\n     * The convex limiter.\n     *\n     * The class implements a convex limiting technique as described in\n     * @cite GuermondEtAl2018,  @cite ryujin-2021-1 and\n     * @cite ryujin-2023-4.\n     * Given a computed set of bounds and an update direction \\f$\\mathbf\n     * P_{ij}\\f$ one can now determine a candidate \\f$\\tilde l_{ij}\\f$ by\n     * computing\n     *\n     * \\f{align}\n     *   \\tilde l_{ij} = \\max_{l\\,\\in\\,[0,1]}\n     *   \\,\\Big\\{\\rho_{\\text{min}}\\,\\le\\,\\rho\\,(\\mathbf U_i +\\tilde\n     * l_{ij}\\mathbf P_{ij})\n     *   \\,\\le\\,\\rho_{\\text{max}},\\quad\n     *   \\phi_{\\text{min}}\\,\\le\\,\\phi\\,(\\mathbf U_{i}+\\tilde l_{ij}\\mathbf\n     * P_{ij})\\Big\\}, \\f}\n     *\n     * where \\f$\\psi\\f$ denots the specific entropy @cite ryujin-2021-1.\n     *\n     * Algorithmically this is accomplished as follows: Given an initial\n     * interval \\f$[t_L,t_R]\\f$, where \\f$t_L\\f$ is a good state, we first\n     * make the interval smaller ensuring the bounds on the density are\n     * fulfilled. If limiting on the specific entropy is selected we then\n     * then perform a quadratic Newton iteration (updating \\f$[t_L,t_R]\\f$\n     * solving for the root of a 3-convex function\n     * \\f{align}\n     *     \\Psi(\\mathbf U)\\;=\\;\\rho^{\\gamma+1}(\\mathbf U)\\,\\big(\\phi(\\mathbf\n     * U)-\\phi_{\\text{min}}\\big). \\f}\n     *\n     * @ingroup EulerEquations\n     */\n    template <int dim, typename Number = double>\n    class Limiter\n    {\n    public:\n      /**\n       * @name Typedefs and constexpr constants\n       */\n      //@{\n\n      using View = HyperbolicSystemView<dim, Number>;\n\n      using ScalarNumber = typename View::ScalarNumber;\n\n      static constexpr auto problem_dimension = View::problem_dimension;\n\n      using state_type = typename View::state_type;\n\n      using flux_contribution_type = typename View::flux_contribution_type;\n\n      using precomputed_type = typename View::precomputed_type;\n\n      using PrecomputedVector = typename View::PrecomputedVector;\n\n      using Parameters = LimiterParameters<ScalarNumber>;\n\n      //@}\n      /**\n       * @name Computation and manipulation of bounds\n       */\n      //\n      //@{\n      /**\n       * The number of stored entries in the bounds array.\n       */\n      static constexpr unsigned int n_bounds = 4;\n\n      /**\n       * Array type used to store accumulated bounds.\n       */\n      using Bounds = std::array<Number, n_bounds>;\n\n      /**\n       * Constructor taking a HyperbolicSystem instance as argument\n       */\n      Limiter(const HyperbolicSystem &hyperbolic_system,\n              const Parameters &parameters,\n              const PrecomputedVector &precomputed_values)\n          : hyperbolic_system(hyperbolic_system)\n          , parameters(parameters)\n          , precomputed_values(precomputed_values)\n      {\n      }\n\n      /**\n       * Given a state @p U_i and an index @p i return \"strict\" bounds,\n       * i.e., a minimal convex set containing the state.\n       */\n      Bounds projection_bounds_from_state(const unsigned int i,\n                                          const state_type &U_i) const;\n\n      /**\n       * Given two bounds bounds_left, bounds_right, this function computes\n       * a larger, combined set of bounds that this is a (convex) superset\n       * of the two.\n       */\n      Bounds combine_bounds(const Bounds &bounds_left,\n                            const Bounds &bounds_right) const;\n\n      /**\n       * This function applies a relaxation to a given a (strict) bound @p\n       * bounds using a non dimensionalized measure @p hd (that should\n       * scale as $h^d$, where $h$ is the local mesh size). This is done\n       * for the case of the Euler equations by multiplying maximum bounds\n       * with $(1+r)$ and minimum bounds with $(1-r)$, while ensuring that\n       * the bounds still describe an admissible state.\n       */\n      Bounds fully_relax_bounds(const Bounds &bounds, const Number &hd) const;\n\n      //@}\n      /**\n       * @name Stencil-based computation of bounds\n       *\n       * Intended usage:\n       * ```\n       * Limiter<dim, Number> limiter;\n       * for (unsigned int i = n_internal; i < n_owned; ++i) {\n       *   // ...\n       *   limiter.reset(i, U_i, flux_i);\n       *   for (unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {\n       *     // ...\n       *     limiter.accumulate(js, U_j, flux_j, scaled_c_ij, affine_shift);\n       *   }\n       *   limiter.bounds(hd_i);\n       * }\n       * ```\n       */\n      //@{\n\n      /**\n       * Reset temporary storage\n       */\n      void reset(const unsigned int i,\n                 const state_type &U_i,\n                 const flux_contribution_type &flux_i);\n\n      /**\n       * When looping over the sparsity row, add the contribution associated\n       * with the neighboring state U_j.\n       */\n      void accumulate(const unsigned int *js,\n                      const state_type &U_j,\n                      const flux_contribution_type &flux_j,\n                      const dealii::Tensor<1, dim, Number> &scaled_c_ij,\n                      const state_type &affine_shift);\n\n      /**\n       * Return the computed bounds (with relaxation applied).\n       */\n      Bounds bounds(const Number hd_i) const;\n\n      //*}\n      /** @name Convex limiter */\n      //@{\n\n      /**\n       * Given a state \\f$\\mathbf U\\f$ and an update \\f$\\mathbf P\\f$ this\n       * function computes and returns the maximal coefficient \\f$t\\f$,\n       * obeying \\f$t_{\\text{min}} < t < t_{\\text{max}}\\f$, such that the\n       * selected local minimum principles are obeyed.\n       *\n       * The returned boolean is set to true if the original low-order\n       * update was within bounds.\n       *\n       * @note If the debug option `DEBUG_EXPENSIVE_BOUNDS_CHECK` is set to\n       * true, then the boolean is set to true if the low-order and the\n       * resulting high-order update are within bounds. The latter might be\n       * violated due to round-off errors when computing the limiter\n       * bounds.\n       */\n      std::tuple<Number, bool> limit(const Bounds &bounds,\n                                     const state_type &U,\n                                     const state_type &P,\n                                     const Number t_min = Number(0.),\n                                     const Number t_max = Number(1.)) const;\n\n    private:\n      //@}\n      /** @name Arguments and internal fields */\n      //@{\n\n      const HyperbolicSystem &hyperbolic_system;\n      const Parameters &parameters;\n      const PrecomputedVector &precomputed_values;\n\n      state_type U_i;\n      flux_contribution_type flux_i;\n\n      Bounds bounds_;\n\n      Number rho_relaxation_numerator;\n      Number rho_relaxation_denominator;\n      Number s_interp_max;\n\n      //@}\n    };\n\n\n    /*\n     * -------------------------------------------------------------------------\n     * Inline definitions\n     * -------------------------------------------------------------------------\n     */\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    Limiter<dim, Number>::projection_bounds_from_state(\n        const unsigned int i, const state_type &U_i) const -> Bounds\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n      const auto rho_i = view.density(U_i);\n      const auto &[p_i, gamma_min_i, s_i, eta_i] =\n          precomputed_values.template read_tensor<Number, precomputed_type>(i);\n\n      return {/*rho_min*/ rho_i,\n              /*rho_max*/ rho_i,\n              /*s_min*/ s_i,\n              /*gamma_min*/ gamma_min_i};\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto Limiter<dim, Number>::combine_bounds(\n        const Bounds &bounds_left, const Bounds &bounds_right) const -> Bounds\n    {\n      const auto &[rho_min_l, rho_max_l, s_min_l, gamma_min_l] = bounds_left;\n      const auto &[rho_min_r, rho_max_r, s_min_r, gamma_min_r] = bounds_right;\n\n      return {std::min(rho_min_l, rho_min_r),\n              std::max(rho_max_l, rho_max_r),\n              std::min(s_min_l, s_min_r),\n              std::min(gamma_min_l, gamma_min_r)};\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    Limiter<dim, Number>::fully_relax_bounds(const Bounds &bounds,\n                                             const Number &hd) const -> Bounds\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      const auto &[rho_min, rho_max, s_min, gamma_min] = bounds;\n\n      auto relaxed_bounds = bounds;\n      auto &[rho_min_relaxed, rho_max_relaxed, s_min_relaxed, g_m_relaxed] =\n          relaxed_bounds;\n\n      /* Use r = factor * (m_i / |Omega|) ^ (1.5 / d): */\n\n      Number r = std::sqrt(hd);                              // in 3D: ^ 3/6\n      if constexpr (dim == 2)                                //\n        r = dealii::Utilities::fixed_power<3>(std::sqrt(r)); // in 2D: ^ 3/4\n      else if constexpr (dim == 1)                           //\n        r = dealii::Utilities::fixed_power<3>(r);            // in 1D: ^ 3/2\n      r *= parameters.relaxation_factor();\n\n      constexpr ScalarNumber eps = std::numeric_limits<ScalarNumber>::epsilon();\n      rho_min_relaxed *= std::max(Number(1.) - r, Number(eps));\n      rho_max_relaxed *= (Number(1.) + r);\n      s_min_relaxed *= std::max(Number(1.) - r, Number(eps));\n\n      /*\n       * If we have a maximum compressibility constant, b, the maximum\n       * bound for rho changes. See @cite ryujin-2023-4 for how to define\n       * rho_max.\n       */\n\n      const auto numerator = (gamma_min + Number(1.)) * rho_max;\n      const auto interpolation_b = view.eos_interpolation_b();\n      const auto denominator =\n          gamma_min - Number(1.) + ScalarNumber(2.) * interpolation_b * rho_max;\n      const auto rho_compressibility_bound = numerator / denominator;\n\n      rho_max_relaxed = std::min(rho_compressibility_bound, rho_max_relaxed);\n\n      return relaxed_bounds;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline void\n    Limiter<dim, Number>::reset(const unsigned int i,\n                                const state_type &new_U_i,\n                                const flux_contribution_type &new_flux_i)\n    {\n      U_i = new_U_i;\n      flux_i = new_flux_i;\n\n      /* Bounds: */\n\n      auto &[rho_min, rho_max, s_min, gamma_min] = bounds_;\n\n      rho_min = Number(std::numeric_limits<ScalarNumber>::max());\n      rho_max = Number(0.);\n      s_min = Number(std::numeric_limits<ScalarNumber>::max());\n\n      const auto &[p_i, gamma_min_i, s_i, eta_i] =\n          precomputed_values.template read_tensor<Number, precomputed_type>(i);\n\n      gamma_min = gamma_min_i;\n\n      /* Relaxation: */\n\n      rho_relaxation_numerator = Number(0.);\n      rho_relaxation_denominator = Number(0.);\n      s_interp_max = Number(0.);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline void Limiter<dim, Number>::accumulate(\n        const unsigned int *js,\n        const state_type &U_j,\n        const flux_contribution_type &flux_j,\n        const dealii::Tensor<1, dim, Number> &scaled_c_ij,\n        const state_type &affine_shift)\n    {\n      // TODO: Currently we only apply the affine_shift to U_ij_bar (which\n      // then enters all bounds), but we do not modify s_interp and\n      // rho_relaxation. When actually adding a source term to the Euler\n      // equations verify that this does the right thing.\n      Assert(std::max(affine_shift.norm(), Number(0.)) == Number(0.),\n             dealii::ExcNotImplemented());\n\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      /* Bounds: */\n      auto &[rho_min, rho_max, s_min, gamma_min] = bounds_;\n\n      const auto rho_i = view.density(U_i);\n      const auto rho_j = view.density(U_j);\n\n      /* bar state shifted by an affine shift: */\n      const auto U_ij_bar =\n          ScalarNumber(0.5) * (U_i + U_j) -\n          ScalarNumber(0.5) * contract(add(flux_j, -flux_i), scaled_c_ij) +\n          affine_shift;\n\n      const auto rho_ij_bar = view.density(U_ij_bar);\n\n      /* Density bounds: */\n\n      rho_min = std::min(rho_min, rho_ij_bar);\n      rho_max = std::max(rho_max, rho_ij_bar);\n\n      /* Density relaxation: */\n\n      /* Use a uniform weight. */\n      const auto beta_ij = Number(1.);\n      rho_relaxation_numerator += beta_ij * (rho_i + rho_j);\n      rho_relaxation_denominator += std::abs(beta_ij);\n\n      /* Surrogate entropy bounds and relaxation: */\n\n      if (view.compute_strict_bounds()) {\n        /*\n         * Compute strict bounds precisely as outlined in @cite ryujin-2023-4\n         *\n         * This means, we compute\n         *  - the surrogate entropy at dof j with the gamma_min of index i,\n         *  - the surrogate entropy of the bar state U_ij_bar\n         *  - an interpolated surrogate entropy at (U_i + U_j) / 2 for\n         *    bounds relaxation:\n         */\n\n        const auto s_j = view.surrogate_specific_entropy(U_j, gamma_min);\n\n        const auto s_ij_bar =\n            view.surrogate_specific_entropy(U_ij_bar, gamma_min);\n\n        const Number s_interp = view.surrogate_specific_entropy(\n            (U_i + U_j) * ScalarNumber(.5), gamma_min);\n\n        s_min = std::min(s_min, s_j);\n        s_min = std::min(s_min, s_ij_bar);\n        s_interp_max = std::max(s_interp_max, s_interp);\n\n      } else {\n        /*\n         * Compute a cheaper bound solely relying on the diagonal s_j\n         * (computed with gamma_min_j) and the surrogate entropy s_ij_bar\n         * of the bar state. We use the s_ij_bar for computing the bounds\n         * relaxation as well.\n         */\n        const auto [p_j, gamma_min_j, s_j, eta_j] =\n            precomputed_values.template read_tensor<Number, precomputed_type>(\n                js);\n\n        const auto s_ij_bar =\n            view.surrogate_specific_entropy(U_ij_bar, gamma_min);\n\n        s_min = std::min(s_min, s_j);\n        s_min = std::min(s_min, s_ij_bar);\n        s_interp_max = std::max(s_interp_max, s_ij_bar);\n      }\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    Limiter<dim, Number>::bounds(const Number hd_i) const -> Bounds\n    {\n      const auto &[rho_min, rho_max, s_min, gamma_min] = bounds_;\n\n      auto relaxed_bounds = fully_relax_bounds(bounds_, hd_i);\n      auto &[rho_min_relaxed, rho_max_relaxed, s_min_relaxed, g_m_relaxed] =\n          relaxed_bounds;\n\n      /* Apply a stricter window: */\n\n      constexpr ScalarNumber eps = std::numeric_limits<ScalarNumber>::epsilon();\n\n      const auto rho_relaxation =\n          ScalarNumber(2. * parameters.relaxation_factor()) *\n          std::abs(rho_relaxation_numerator) /\n          (std::abs(rho_relaxation_denominator) + Number(eps));\n\n      const auto entropy_relaxation =\n          parameters.relaxation_factor() * (s_interp_max - s_min);\n\n      rho_min_relaxed = std::max(rho_min_relaxed, rho_min - rho_relaxation);\n      rho_max_relaxed = std::min(rho_max_relaxed, rho_max + rho_relaxation);\n      s_min_relaxed = std::max(s_min_relaxed, s_min - entropy_relaxation);\n\n      return relaxed_bounds;\n    }\n  } // namespace EulerAEOS\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_aeos/limiter.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include \"limiter.h\"\n// #define DEBUG_OUTPUT_LIMITER\n\nnamespace ryujin\n{\n  namespace EulerAEOS\n  {\n    template <int dim, typename Number>\n    std::tuple<Number, bool>\n    Limiter<dim, Number>::limit(const Bounds &bounds,\n                                const state_type &U,\n                                const state_type &P,\n                                const Number t_min /* = Number(0.) */,\n                                const Number t_max /* = Number(1.) */) const\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      bool success = true;\n      Number t_r = t_max;\n\n      constexpr ScalarNumber eps = std::numeric_limits<ScalarNumber>::epsilon();\n      const auto small = view.vacuum_state_relaxation_small();\n      const auto large = view.vacuum_state_relaxation_large();\n      const ScalarNumber relax_small = ScalarNumber(1. + small * eps);\n      const ScalarNumber relax = ScalarNumber(1. + large * eps);\n\n      /*\n       * First limit the density rho.\n       *\n       * See [Guermond, Nazarov, Popov, Thomas] (4.8):\n       */\n\n      {\n        const auto &rho_U = view.density(U);\n        const auto &rho_P = view.density(P);\n\n        const auto &rho_min = std::get<0>(bounds);\n        const auto &rho_max = std::get<1>(bounds);\n\n        /*\n         * Verify that rho_U is within bounds. This property might be\n         * violated for relative CFL numbers larger than 1.\n         */\n        const auto test_min = view.filter_vacuum_density(\n            std::max(Number(0.), rho_U - relax * rho_max));\n        const auto test_max = view.filter_vacuum_density(\n            std::max(Number(0.), rho_min - relax * rho_U));\n        if (!(test_min == Number(0.) && test_max == Number(0.))) {\n#ifdef DEBUG_OUTPUT\n          std::cout << std::fixed << std::setprecision(16);\n          std::cout << \"Bounds violation: low-order density (critical)!\"\n                    << \"\\n\\t\\trho min:         \" << rho_min\n                    << \"\\n\\t\\trho min (delta): \"\n                    << negative_part(rho_U - rho_min)\n                    << \"\\n\\t\\trho:             \" << rho_U\n                    << \"\\n\\t\\trho max (delta): \"\n                    << positive_part(rho_U - rho_max)\n                    << \"\\n\\t\\trho max:         \" << rho_max << \"\\n\"\n                    << std::endl;\n#endif\n          success = false;\n        }\n\n        const Number denominator =\n            ScalarNumber(1.) / (std::abs(rho_P) + eps * rho_max);\n\n        t_r = dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(\n            rho_max,\n            rho_U + t_r * rho_P,\n            /*\n             * rho_P is positive.\n             *\n             * Note: Do not take an absolute value here. If we are out of\n             * bounds we have to ensure that t_r is set to t_min.\n             */\n            (rho_max - rho_U) * denominator,\n            t_r);\n\n        t_r = dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(\n            rho_U + t_r * rho_P,\n            rho_min,\n            /*\n             * rho_P is negative.\n             *\n             * Note: Do not take an absolute value here. If we are out of\n             * bounds we have to ensure that t_r is set to t_min.\n             */\n            (rho_U - rho_min) * denominator,\n            t_r);\n\n        /*\n         * Ensure that t_min <= t <= t_max. This might not be the case if\n         * rho_U is outside the interval [rho_min, rho_max]. Furthermore,\n         * the quotient we take above is prone to numerical cancellation in\n         * particular in the second pass of the limiter when rho_P might be\n         * small.\n         */\n        t_r = std::min(t_r, t_max);\n        t_r = std::max(t_r, t_min);\n\n#ifdef DEBUG_EXPENSIVE_BOUNDS_CHECK\n        /*\n         * Verify that the new state is within bounds:\n         */\n        const auto rho_new = view.density(U + t_r * P);\n        const auto test_new_min = view.filter_vacuum_density(\n            std::max(Number(0.), rho_new - relax * rho_max));\n        const auto test_new_max = view.filter_vacuum_density(\n            std::max(Number(0.), rho_min - relax * rho_new));\n        if (!(test_new_min == Number(0.) && test_new_max == Number(0.))) {\n#ifdef DEBUG_OUTPUT\n          std::cout << std::fixed << std::setprecision(16);\n          std::cout << \"Bounds violation: high-order density!\"\n                    << \"\\n\\t\\trho min:         \" << rho_min\n                    << \"\\n\\t\\trho min (delta): \"\n                    << negative_part(rho_new - rho_min)\n                    << \"\\n\\t\\trho:             \" << rho_new\n                    << \"\\n\\t\\trho max (delta): \"\n                    << positive_part(rho_new - rho_max)\n                    << \"\\n\\t\\trho max:         \" << rho_max << \"\\n\"\n                    << std::endl;\n#endif\n          success = false;\n        }\n#endif\n      }\n\n      /*\n       * Then limit the specific entropy:\n       *\n       * See [Guermond, Nazarov, Popov, Thomas],\n       * Section 4.6 + Section 5.1\n       * and @cite clayton2023robust Section 6:\n       */\n\n      Number t_l = t_min; // good state\n\n      const auto &gamma = std::get<3>(bounds) /* = gamma_min*/;\n      const Number gm1 = gamma - Number(1.);\n\n      const auto b = Number(view.eos_interpolation_b());\n      const auto pinf = Number(view.eos_interpolation_pinfty());\n      const auto q = Number(view.eos_interpolation_q());\n\n      {\n        /*\n         * Prepare a quadratic Newton method:\n         *\n         * Given initial limiter values t_l and t_r with psi(t_l) > 0 and\n         * psi(t_r) < 0 we try to find t^\\ast with psi(t^\\ast) \\approx 0.\n         *\n         * Here, psi is a 3-convex function obtained by scaling the specific\n         * entropy s:\n         *\n         *   psi = \\rho ^ {\\gamma + 1} s\n         *\n         * (s in turn was defined as s =\\varepsilon \\rho ^{-\\gamma}, where\n         * \\varepsilon = (\\rho e - pinf * (1 - b rho)) is the shifted\n         * internal energy.)\n         */\n\n        const auto &s_min = std::get<2>(bounds);\n\n#ifdef DEBUG_OUTPUT_LIMITER\n        std::cout << std::endl;\n        std::cout << std::fixed << std::setprecision(16);\n        std::cout << \"t_l: (start) \" << t_l << std::endl;\n        std::cout << \"t_r: (start) \" << t_r << std::endl;\n#endif\n\n        for (unsigned int n = 0; n < parameters.newton_max_iterations(); ++n) {\n\n          const auto U_r = U + t_r * P;\n          const auto rho_r = view.density(U_r);\n          const auto rho_r_gamma = ryujin::pow(rho_r, gamma);\n          const auto covolume_r = Number(1.) - b * rho_r;\n\n          const auto rho_e_r = view.internal_energy(U_r);\n          const auto shift_r = rho_e_r - rho_r * q - pinf * covolume_r;\n\n          auto psi_r =\n              relax_small * rho_r * shift_r -\n              s_min * rho_r * rho_r_gamma * ryujin::pow(covolume_r, -gm1);\n\n#ifndef DEBUG_EXPENSIVE_BOUNDS_CHECK\n          /*\n           * If psi_r > 0 the right state is fine, force returning t_r by\n           * setting t_l = t_r:\n           */\n          t_l = dealii::compare_and_apply_mask<\n              dealii::SIMDComparison::greater_than>(\n              psi_r, Number(0.), t_r, t_l);\n\n          /*\n           * If we have set t_l = t_r everywhere then all states state U_r\n           * with t_r obey the specific entropy inequality and we can\n           * break.\n           *\n           * This is a very important optimization: Only for 1 in (25 to\n           * 50) cases do we actually need to limit on the specific entropy\n           * because one of the right states failed. So we can skip\n           * constructing the left state U_l, which is expensive.\n           *\n           * This implies unfortunately that we might not accurately report\n           * whether the low_order update U itself obeyed bounds because\n           * U_r = U + t_r * P pushed us back into bounds. We thus skip\n           * this shortcut if `DEBUG_EXPENSIVE_BOUNDS_CHECK` is set.\n           */\n          if (t_l == t_r) {\n#ifdef DEBUG_OUTPUT_LIMITER\n            std::cout << \"shortcut: t_l == t_r\" << std::endl;\n            std::cout << \"psi_l:       \" << psi_l << std::endl;\n            std::cout << \"psi_r:       \" << psi_r << std::endl;\n            std::cout << \"t_l: (  \" << n << \"  ) \" << t_l << std::endl;\n            std::cout << \"t_r: (  \" << n << \"  ) \" << t_r << std::endl;\n#endif\n            break;\n          }\n#endif\n\n          const auto U_l = U + t_l * P;\n          const auto rho_l = view.density(U_l);\n          const auto rho_l_gamma = ryujin::pow(rho_l, gamma);\n          const auto covolume_l = Number(1.) - b * rho_l;\n          const auto rho_e_l = view.internal_energy(U_l);\n          const auto shift_l = rho_e_l - rho_l * q - pinf * covolume_l;\n\n          auto psi_l =\n              relax_small * rho_l * shift_l -\n              s_min * rho_l * rho_l_gamma * ryujin::pow(covolume_l, -gm1);\n\n          /*\n           * Verify that the left state is within bounds. This property might\n           * be violated for relative CFL numbers larger than 1.\n           */\n          const auto lower_bound = (ScalarNumber(1.) - relax) * s_min * rho_l *\n                                   rho_l_gamma * ryujin::pow(covolume_l, -gm1);\n          if (n == 0 &&\n              !(std::min(Number(0.), psi_l - lower_bound) == Number(0.))) {\n#ifdef DEBUG_OUTPUT\n            std::cout << std::fixed << std::setprecision(16);\n            std::cout\n                << \"Bounds violation: low-order specific entropy (critical)!\\n\";\n            std::cout << \"\\t\\tPsi left: 0 <= \" << psi_l << \"\\n\" << std::endl;\n#endif\n            success = false;\n          }\n\n#ifdef DEBUG_EXPENSIVE_BOUNDS_CHECK\n          /*\n           * If psi_r > 0 the right state is fine, force returning t_r by\n           * setting t_l = t_r:\n           */\n          t_l = dealii::compare_and_apply_mask<\n              dealii::SIMDComparison::greater_than>(\n              psi_r, Number(0.), t_r, t_l);\n#endif\n\n          /*\n           * Break if the window between t_l and t_r is within the prescribed\n           * tolerance:\n           */\n          const Number tolerance(parameters.newton_tolerance());\n          if (std::max(Number(0.), t_r - t_l - tolerance) == Number(0.)) {\n#ifdef DEBUG_OUTPUT_LIMITER\n            std::cout << \"break: t_l and t_r within tolerance\" << std::endl;\n            std::cout << \"psi_l:       \" << psi_l << std::endl;\n            std::cout << \"psi_r:       \" << psi_r << std::endl;\n            std::cout << \"t_l: (  \" << n << \"  ) \" << t_l << std::endl;\n            std::cout << \"t_r: (  \" << n << \"  ) \" << t_r << std::endl;\n#endif\n            break;\n          }\n\n          /* We got unlucky and have to perform a Newton step: */\n\n          const auto drho = view.density(P);\n          const auto drho_e_l = view.internal_energy_derivative(U_l) * P;\n          const auto drho_e_r = view.internal_energy_derivative(U_r) * P;\n\n          const auto q_pinf_term_l =\n              ScalarNumber(2.) * rho_l * q +\n              pinf * (Number(1.) - ScalarNumber(2.) * b * rho_l);\n          const auto q_pinf_term_r =\n              ScalarNumber(2.) * rho_r * q +\n              pinf * (Number(1.) - ScalarNumber(2.) * b * rho_r);\n\n          const auto extra_term_l = s_min *\n                                    ryujin::pow(rho_l / covolume_l, gamma) *\n                                    (covolume_l + gamma - b * rho_l);\n          const auto extra_term_r = s_min *\n                                    ryujin::pow(rho_r / covolume_r, gamma) *\n                                    (covolume_r + gamma - b * rho_r);\n\n          const auto dpsi_l = rho_l * drho_e_l +\n                              (rho_e_l - q_pinf_term_l - extra_term_l) * drho;\n          const auto dpsi_r = rho_r * drho_e_r +\n                              (rho_e_r - q_pinf_term_r - extra_term_r) * drho;\n\n          quadratic_newton_step(\n              t_l, t_r, psi_l, psi_r, dpsi_l, dpsi_r, Number(-1.));\n\n#ifdef DEBUG_OUTPUT_LIMITER\n          std::cout << \"psi_l:       \" << psi_l << std::endl;\n          std::cout << \"psi_r:       \" << psi_r << std::endl;\n          std::cout << \"dpsi_l:      \" << dpsi_l << std::endl;\n          std::cout << \"dpsi_r:      \" << dpsi_r << std::endl;\n          std::cout << \"t_l: (  \" << n << \"  ) \" << t_l << std::endl;\n          std::cout << \"t_r: (  \" << n << \"  ) \" << t_r << std::endl;\n#endif\n        }\n\n#ifdef DEBUG_EXPENSIVE_BOUNDS_CHECK\n        /*\n         * Verify that the new state is within bounds:\n         */\n        {\n          const auto U_new = U + t_l * P;\n\n          const auto rho_new = view.density(U_new);\n          const auto covolume_new = Number(1.) - b * rho_new;\n\n          const auto rho_new_gamma = ryujin::pow(rho_new, gamma);\n          const auto rho_e_new = view.internal_energy(U_new);\n\n          const auto shift_new = rho_e_new - rho_new * q - pinf * covolume_new;\n\n          const auto psi_new =\n              relax_small * rho_new * shift_new -\n              s_min * rho_new * rho_new_gamma * ryujin::pow(covolume_new, -gm1);\n\n          const auto lower_bound = (ScalarNumber(1.) - relax) * s_min *\n                                   rho_new * rho_new_gamma *\n                                   ryujin::pow(covolume_new, -gm1);\n\n          const bool e_valid = std::min(Number(0.), shift_new) == Number(0.);\n          const bool psi_valid =\n              std::min(Number(0.), psi_new - lower_bound) == Number(0.);\n\n          if (!e_valid || !psi_valid) {\n#ifdef DEBUG_OUTPUT\n            std::cout << std::fixed << std::setprecision(16);\n            std::cout << \"Bounds violation: high-order specific entropy!\\n\";\n            std::cout << \"\\t\\trho e: 0 <= \" << rho_e_new << \"\\n\";\n            std::cout << \"\\t\\tPsi:   0 <= \" << psi_new << \"\\n\" << std::endl;\n#endif\n            success = false;\n          }\n        }\n#endif\n      }\n\n      return {t_l, success};\n    }\n\n  } // namespace EulerAEOS\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_aeos/riemann_solver.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2023 by the ryujin authors\n//\n\n#include \"riemann_solver.template.h\"\n\n#include <deal.II/base/vectorization.h>\n\nnamespace ryujin\n{\n  namespace EulerAEOS\n  {\n    /* instantiations */\n\n    template class RiemannSolver<1, NUMBER>;\n    template class RiemannSolver<2, NUMBER>;\n    template class RiemannSolver<3, NUMBER>;\n\n    template class RiemannSolver<1, dealii::VectorizedArray<NUMBER>>;\n    template class RiemannSolver<2, dealii::VectorizedArray<NUMBER>>;\n    template class RiemannSolver<3, dealii::VectorizedArray<NUMBER>>;\n  } // namespace EulerAEOS\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_aeos/riemann_solver.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"hyperbolic_system.h\"\n\n#include <simd.h>\n\n#include <deal.II/base/point.h>\n#include <deal.II/base/tensor.h>\n\nnamespace ryujin\n{\n  namespace EulerAEOS\n  {\n    template <typename ScalarNumber = double>\n    class RiemannSolverParameters : public dealii::ParameterAcceptor\n    {\n    public:\n      RiemannSolverParameters(const std::string &subsection = \"/RiemannSolver\")\n          : ParameterAcceptor(subsection)\n      {\n      }\n    };\n\n\n    /**\n     * A fast approximative solver for the 1D Riemann problem. The solver\n     * ensures that the estimate \\f$\\lambda_{\\text{max}}\\f$ that is returned\n     * for the maximal wavespeed is a strict upper bound.\n     *\n     * The solver is based on @cite ClaytonGuermondPopov-2022.\n     *\n     * @ingroup EulerEquations\n     */\n    template <int dim, typename Number = double>\n    class RiemannSolver\n    {\n    public:\n      /**\n       * @name Typedefs and constexpr constants\n       */\n      //@{\n\n      using View = HyperbolicSystemView<dim, Number>;\n\n      using ScalarNumber = typename View::ScalarNumber;\n\n      static constexpr auto problem_dimension = View::problem_dimension;\n\n      using state_type = typename View::state_type;\n\n      /**\n       * Number of components in a primitive state, we store \\f$[\\rho, v,\n       * p, a, gamma]\\f$, thus, 5.\n       */\n      static constexpr unsigned int riemann_data_size = 5;\n\n      /**\n       * The array type to store the expanded primitive state for the\n       * Riemann solver \\f$[\\rho, v, p, a]\\f$\n       */\n      using primitive_type = typename std::array<Number, riemann_data_size>;\n\n      using precomputed_type = typename View::precomputed_type;\n\n      using PrecomputedVector = typename View::PrecomputedVector;\n\n      using Parameters = RiemannSolverParameters<ScalarNumber>;\n\n      //@}\n      /**\n       * @name Compute wavespeed estimates\n       */\n      //@{\n\n      /**\n       * Constructor taking a HyperbolicSystem instance as argument\n       */\n      RiemannSolver(const HyperbolicSystem &hyperbolic_system,\n                    const Parameters &parameters,\n                    const PrecomputedVector &precomputed_values)\n          : hyperbolic_system(hyperbolic_system)\n          , parameters(parameters)\n          , precomputed_values(precomputed_values)\n      {\n      }\n\n      /**\n       * For two given 1D primitive states riemann_data_i and\n       * riemann_data_j, compute an estimate for an upper bound of the\n       * maximum wavespeed lambda.\n       */\n      Number compute(const primitive_type &riemann_data_i,\n                     const primitive_type &riemann_data_j) const;\n\n      /**\n       * For two given states U_i a U_j and a (normalized) \"direction\" n_ij\n       * compute an estimate for an upper bound of the maximum wavespeed\n       * lambda.\n       */\n      Number compute(const state_type &U_i,\n                     const state_type &U_j,\n                     const unsigned int i,\n                     const unsigned int *js,\n                     const dealii::Tensor<1, dim, Number> &n_ij) const;\n\n      //@}\n\n    protected:\n      /** @name Internal functions used in the Riemann solver */\n      //@{\n\n      /**\n       * FIXME\n       *\n       * Cost: 0x pow, 1x division, 1x sqrt\n       */\n      Number c(const Number &gamma_Z) const;\n\n      /**\n       * FIXME\n       *\n       * Cost: 0x pow, 1x division, 0x sqrt\n       */\n      Number\n      alpha(const Number &rho, const Number &gamma, const Number &a) const;\n\n      /**\n       * Compute the best available, but expensive, upper bound on the\n       * expansion-shock case as described in §5.4, Eqn. (5.7) and (5.8) in\n       * @cite ClaytonGuermondPopov-2022\n       *\n       * Cost: 5x pow, 11x division, 1x sqrt\n       */\n      Number p_star_RS_full(const primitive_type &riemann_data_i,\n                            const primitive_type &riemann_data_j) const;\n\n      /**\n       * Compute the best available, but expensive, upper bound on the\n       * shock-shock case as described in §5.5, Eqn. (5.10) and (5.12) in\n       * @cite ClaytonGuermondPopov-2022\n       *\n       * Cost: 2x pow, 9x division, 3x sqrt\n       */\n      Number p_star_SS_full(const primitive_type &riemann_data_i,\n                            const primitive_type &riemann_data_j) const;\n\n      /*\n       * Compute only the failsafe the failsafe bound for \\f$\\tilde\n       * p_2^\\ast\\f$ (5.11) in @cite ClaytonGuermondPopov-2022\n       *\n       * Cost: 0x pow, 3x division, 3x sqrt\n       */\n      Number p_star_failsafe(const primitive_type &riemann_data_i,\n                             const primitive_type &riemann_data_j) const;\n\n      /*\n       * Compute a simultaneous upper bound on (5.7) second formula for\n       * \\tilde p_2^\\ast (5.8) first formula for \\tilde p_1^\\ast (5.11)\n       * formula for \\tilde p_2^\\ast in @cite ClaytonGuermondPopov-2022\n       *\n       * Cost: 3x pow, 9x division, 2x sqrt\n       *\n       * @todo improve documentation\n       */\n      Number p_star_interpolated(const primitive_type &riemann_data_i,\n                                 const primitive_type &riemann_data_j) const;\n\n\n#ifndef DOXYGEN\n      /*\n       * FIXME\n       */\n      Number f(const primitive_type &riemann_data, const Number p_star) const;\n\n\n      /*\n       * FIXME\n       */\n      Number phi(const primitive_type &riemann_data_i,\n                 const primitive_type &riemann_data_j,\n                 const Number p_in) const;\n#endif\n\n\n      /**\n       * See @cite ClaytonGuermondPopov-2022\n       *\n       * The approximate Riemann solver is based on a function phi(p) that is\n       * montone increasing in p, concave down and whose (weak) third\n       * derivative is non-negative and locally bounded. Because we\n       * actually do not perform any iteration for computing our wavespeed\n       * estimate we can get away by only implementing a specialized\n       * variant of the phi function that computes phi(p_max). It inlines\n       * the implementation of the \"f\" function and eliminates all\n       * unnecessary branches in \"f\".\n       *\n       * Cost: 0x pow, 2x division, 2x sqrt\n       */\n      Number phi_of_p_max(const primitive_type &riemann_data_i,\n                          const primitive_type &riemann_data_j) const;\n\n\n      /**\n       * See @cite GuermondPopov2016 page 912, (3.7)\n       *\n       * Cost: 0x pow, 1x division, 1x sqrt\n       */\n      Number lambda1_minus(const primitive_type &riemann_data,\n                           const Number p_star) const;\n\n\n      /**\n       * See @cite GuermondPopov2016 page 912, (3.8)\n       *\n       * Cost: 0x pow, 1x division, 1x sqrt\n       */\n      Number lambda3_plus(const primitive_type &primitive_state,\n                          const Number p_star) const;\n\n\n      /**\n       * See @cite GuermondPopov2016 page 912, (3.9)\n       *\n       * For two given primitive states <code>riemann_data_i</code> and\n       * <code>riemann_data_j</code>, and a guess p_2, compute an upper bound\n       * for lambda.\n       *\n       * Cost: 0x pow, 2x division, 2x sqrt (inclusive)\n       */\n      Number compute_lambda(const primitive_type &riemann_data_i,\n                            const primitive_type &riemann_data_j,\n                            const Number p_star) const;\n\n\n      /**\n       * For a given (2+dim dimensional) state vector <code>U</code>, and a\n       * (normalized) \"direction\" n_ij, first compute the corresponding\n       * projected state in the corresponding 1D Riemann problem, and then\n       * compute and return the Riemann data [rho, u, p, a] (used in the\n       * approximative Riemann solver).\n       */\n      primitive_type\n      riemann_data_from_state(const state_type &U,\n                              const Number &p,\n                              const dealii::Tensor<1, dim, Number> &n_ij) const;\n\n    private:\n      const HyperbolicSystem &hyperbolic_system;\n      const Parameters &parameters;\n      const PrecomputedVector &precomputed_values;\n      //@}\n    };\n  } // namespace EulerAEOS\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/euler_aeos/riemann_solver.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include \"riemann_solver.h\"\n\n#include <newton.h>\n#include <simd.h>\n\n// #define DEBUG_RIEMANN_SOLVER\n\nnamespace ryujin\n{\n  namespace EulerAEOS\n  {\n    /*\n     * The RiemannSolver is a guaranteed maximal wavespeed (GMS) estimate\n     * for the extended Riemann problem outlined in\n     * @cite ClaytonGuermondPopov-2022. For extenstions on handling negative\n     * pressures, we follow @cite clayton2023robust (see §4.6).\n     *\n     * In contrast to the algorithm outlined in above reference the\n     * algorithm takes a couple of shortcuts to significantly decrease the\n     * computational footprint. These simplifications still guarantee that\n     * we have an upper bound on the maximal wavespeed - but the number\n     * bound might be larger. In particular:\n     *\n     *  - We do not check and treat the case phi(p_min) > 0. This\n     *    corresponds to two expansion waves, see §5.2 in the reference. In\n     *    this case we have\n     *\n     *      0 < p_star < p_min <= p_max.\n     *\n     *    And due to the fact that p_star < p_min the wavespeeds reduce to\n     *    a left wavespeed v_L - a_L and right wavespeed v_R + a_R. This\n     *    implies that it is sufficient to set p_2 to ANY value provided\n     *    that p_2 <= p_min hold true in order to compute the correct\n     *    wavespeed.\n     *\n     *    If p_2 > p_min then a more pessimistic bound is computed.\n     *\n     *  - FIXME: Simplification in p_star_RS\n     */\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::c(const Number &gamma) const\n    {\n      /*\n       * We implement the continuous and monotonic function c(gamma) as\n       * defined in (A.3) on page A469 of @cite ClaytonGuermondPopov-2022.\n       * But with a simplified quick cut-off for the case gamma > 3:\n       *\n       *   c(gamma)^2 = 1                                    for gamma <= 5 / 3\n       *   c(gamma)^2 = (3 * gamma + 11) / (6 * gamma + 6)   in between\n       *   c(gamma)^2 = max(1/2, 5 / 6 - slope (gamma - 3))  for gamma > 3\n       *\n       * Due to the fact that the function is monotonic we can simply clip\n       * the values without checking the conditions:\n       */\n\n      constexpr ScalarNumber slope =\n          ScalarNumber(-0.34976871477801828189920753948709);\n\n      const Number first_radicand = (ScalarNumber(3.) * gamma + Number(11.)) /\n                                    (ScalarNumber(6.) * gamma + Number(6.));\n\n      const Number second_radicand =\n          Number(5. / 6.) + slope * (gamma - Number(3.));\n\n      Number radicand = std::min(first_radicand, second_radicand);\n      radicand = std::min(Number(1.), radicand);\n      radicand = std::max(Number(1. / 2.), radicand);\n\n      return std::sqrt(radicand);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number RiemannSolver<dim, Number>::alpha(\n        const Number &rho, const Number &gamma, const Number &a) const\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n      const auto interpolation_b = view.eos_interpolation_b();\n\n      const Number numerator =\n          ScalarNumber(2.) * a * (Number(1.) - interpolation_b * rho);\n\n      const Number denominator = gamma - Number(1.);\n\n      return safe_division(numerator, denominator);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::p_star_RS_full(\n        const primitive_type &riemann_data_i,\n        const primitive_type &riemann_data_j) const\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n      const auto pinf = view.eos_interpolation_pinfty();\n\n      const auto &[rho_i, u_i, p_i, gamma_i, a_i] = riemann_data_i;\n      const auto &[rho_j, u_j, p_j, gamma_j, a_j] = riemann_data_j;\n      const auto alpha_i = alpha(rho_i, gamma_i, a_i);\n      const auto alpha_j = alpha(rho_j, gamma_j, a_j);\n\n      /*\n       * First get p_min, p_max.\n       *\n       * Then, we get gamma_min/max, and alpha_min/max. Note that the\n       * *_min/max values are associated with p_min/max and are not\n       * necessarily the minimum/maximum of *_i vs *_j.\n       */\n\n      const Number p_min = std::min(p_i, p_j);\n      const Number p_max = std::max(p_i, p_j);\n\n      const Number gamma_min =\n          dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(\n              p_i, p_j, gamma_i, gamma_j);\n\n      const Number alpha_min =\n          dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(\n              p_i, p_j, alpha_i, alpha_j);\n\n      const Number alpha_hat_min = c(gamma_min) * alpha_min;\n\n      const Number alpha_max = dealii::compare_and_apply_mask<\n          dealii::SIMDComparison::greater_than_or_equal>(\n          p_i, p_j, alpha_i, alpha_j);\n\n      const Number gamma_m = std::min(gamma_i, gamma_j);\n      const Number gamma_M = std::max(gamma_i, gamma_j);\n\n      const Number numerator =\n          dealii::compare_and_apply_mask<dealii::SIMDComparison::equal>(\n              p_max + pinf,\n              Number(0.),\n              Number(0.),\n              positive_part(alpha_hat_min + alpha_max - (u_j - u_i)));\n\n      /*\n       * The admissible set is p_min >= pinf. But numerically let's avoid\n       * division by zero and ensure positivity:\n       */\n      const Number p_ratio = safe_division(p_min + pinf, p_max + pinf);\n\n      /*\n       * Here, we use a trick: The r-factor only shows up in the formula\n       * for the case \\gamma_min = \\gamma_m, otherwise the r-factor\n       * vanishes. We can accomplish this by using the following modified\n       * exponent (where we substitute gamma_m by gamma_min):\n       */\n      const Number r_exponent =\n          (gamma_M - gamma_min) / (ScalarNumber(2.) * gamma_min * gamma_M);\n\n      /*\n       * Compute (5.7) first formula for \\tilde p_1^\\ast and (5.8)\n       * second formula for \\tilde p_2^\\ast at the same time:\n       */\n\n      const Number first_exponent =\n          (gamma_M - Number(1.)) / (ScalarNumber(2.) * gamma_M);\n\n      const Number first_exponent_inverse =\n          safe_division(Number(1.), first_exponent);\n\n      const Number first_denom =\n          alpha_hat_min * ryujin::pow(p_ratio, r_exponent - first_exponent) +\n          alpha_max;\n\n      const Number p_1_tilde =\n          (p_max + pinf) * ryujin::pow(safe_division(numerator, first_denom),\n                                       first_exponent_inverse) -\n          pinf;\n\n#ifdef DEBUG_RIEMANN_SOLVER\n      std::cout << \"RS p_1_tilde  = \" << p_1_tilde << \"\\n\";\n#endif\n\n      /*\n       * Compute (5.7) second formula for \\tilde p_2^\\ast and (5.8) first\n       * formula for \\tilde p_1^\\ast at the same time:\n       */\n\n      const Number second_exponent =\n          (gamma_m - Number(1.)) / (ScalarNumber(2.) * gamma_m);\n\n      const Number second_exponent_inverse =\n          safe_division(Number(1.), second_exponent);\n\n      Number second_denom =\n          alpha_hat_min * ryujin::pow(p_ratio, -second_exponent) +\n          alpha_max * ryujin::pow(p_ratio, r_exponent);\n\n      const Number p_2_tilde =\n          (p_max + pinf) * ryujin::pow(safe_division(numerator, second_denom),\n                                       second_exponent_inverse) -\n          pinf;\n\n#ifdef DEBUG_RIEMANN_SOLVER\n      std::cout << \"RS p_2_tilde  = \" << p_2_tilde << \"\\n\";\n#endif\n\n      return std::min(p_1_tilde, p_2_tilde);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::p_star_SS_full(\n        const primitive_type &riemann_data_i,\n        const primitive_type &riemann_data_j) const\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n      const auto pinf = view.eos_interpolation_pinfty();\n\n      const auto &[rho_i, u_i, p_i, gamma_i, a_i] = riemann_data_i;\n      const auto &[rho_j, u_j, p_j, gamma_j, a_j] = riemann_data_j;\n\n      const Number gamma_m = std::min(gamma_i, gamma_j);\n\n      const Number alpha_hat_i = c(gamma_i) * alpha(rho_i, gamma_i, a_i);\n      const Number alpha_hat_j = c(gamma_j) * alpha(rho_j, gamma_j, a_j);\n\n      /*\n       * Compute (5.10) formula for \\tilde p_1^\\ast:\n       *\n       * Cost: 2x pow, 4x division, 0x sqrt\n       */\n\n      const Number exponent =\n          (gamma_m - Number(1.)) / (ScalarNumber(2.) * gamma_m);\n      const Number exponent_inverse = Number(1.) / exponent;\n\n      const Number numerator =\n          dealii::compare_and_apply_mask<dealii::SIMDComparison::equal>(\n              p_j + pinf,\n              Number(0.),\n              Number(0.),\n              positive_part(alpha_hat_i + alpha_hat_j - (u_j - u_i)));\n\n      const Number denominator =\n          alpha_hat_i *\n              ryujin::pow(safe_division(p_i + pinf, p_j + pinf), -exponent) +\n          alpha_hat_j;\n\n      const Number p_1_tilde =\n          (p_j + pinf) * ryujin::pow(safe_division(numerator, denominator),\n                                     exponent_inverse) -\n          pinf;\n\n#ifdef DEBUG_RIEMANN_SOLVER\n      std::cout << \"SS p_1_tilde  = \" << p_1_tilde << \"\\n\";\n#endif\n\n      const auto p_2_tilde = p_star_failsafe(riemann_data_i, riemann_data_j);\n\n      return std::min(p_1_tilde, p_2_tilde);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::p_star_failsafe(\n        const primitive_type &riemann_data_i,\n        const primitive_type &riemann_data_j) const\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n      const auto interpolation_b = view.eos_interpolation_b();\n      const auto pinf = view.eos_interpolation_pinfty();\n\n      const auto &[rho_i, u_i, p_i, gamma_i, a_i] = riemann_data_i;\n      const auto &[rho_j, u_j, p_j, gamma_j, a_j] = riemann_data_j;\n\n      /*\n       * Compute (5.11) formula for \\tilde p_2^\\ast:\n       *\n       * Cost: 0x pow, 3x division, 3x sqrt\n       */\n\n      const Number p_max = std::max(p_i, p_j) + pinf;\n\n      const Number radicand_i = safe_division(\n          ScalarNumber(2.) * (Number(1.) - interpolation_b * rho_i) * p_max,\n          rho_i * ((gamma_i + Number(1.)) * p_max +\n                   (gamma_i - Number(1.)) * (p_i + pinf)));\n\n      const Number x_i = std::sqrt(radicand_i);\n\n      const Number radicand_j = safe_division(\n          ScalarNumber(2.) * (Number(1.) - interpolation_b * rho_j) * p_max,\n          rho_j * ((gamma_j + Number(1.)) * p_max +\n                   (gamma_j - Number(1.)) * (p_j + pinf)));\n\n      const Number x_j = std::sqrt(radicand_j);\n\n      const Number a = x_i + x_j;\n      const Number b =\n          dealii::compare_and_apply_mask<dealii::SIMDComparison::equal>(\n              a, Number(0.), Number(0.), u_j - u_i);\n\n      const Number c = -(p_i + pinf) * x_i - (p_j + pinf) * x_j;\n\n      const Number base = safe_division(\n          std::abs(-b +\n                   std::sqrt(positive_part(b * b - ScalarNumber(4.) * a * c))),\n          std::abs(ScalarNumber(2.) * a));\n\n      const Number p_2_tilde = base * base - pinf;\n\n#ifdef DEBUG_RIEMANN_SOLVER\n      std::cout << \"SS p_2_tilde  = \" << p_2_tilde << \"\\n\";\n#endif\n      return p_2_tilde;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::p_star_interpolated(\n        const primitive_type &riemann_data_i,\n        const primitive_type &riemann_data_j) const\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n      const auto pinf = view.eos_interpolation_pinfty();\n\n      const auto &[rho_i, u_i, p_i, gamma_i, a_i] = riemann_data_i;\n      const auto &[rho_j, u_j, p_j, gamma_j, a_j] = riemann_data_j;\n      const auto alpha_i = alpha(rho_i, gamma_i, a_i);\n      const auto alpha_j = alpha(rho_j, gamma_j, a_j);\n\n      /*\n       * First get p_min, p_max.\n       *\n       * Then, we get gamma_min/max, and alpha_min/max. Note that the\n       * *_min/max values are associated with p_min/max and are not\n       * necessarily the minimum/maximum of *_i vs *_j.\n       */\n\n      const Number p_min = std::min(p_i, p_j) + pinf;\n      const Number p_max = std::max(p_i, p_j) + pinf;\n\n      const Number gamma_min =\n          dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(\n              p_i, p_j, gamma_i, gamma_j);\n\n      const Number alpha_min =\n          dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(\n              p_i, p_j, alpha_i, alpha_j);\n\n      const Number alpha_hat_min = c(gamma_min) * alpha_min;\n\n      const Number gamma_max = dealii::compare_and_apply_mask<\n          dealii::SIMDComparison::greater_than_or_equal>(\n          p_i, p_j, gamma_i, gamma_j);\n\n      const Number alpha_max = dealii::compare_and_apply_mask<\n          dealii::SIMDComparison::greater_than_or_equal>(\n          p_i, p_j, alpha_i, alpha_j);\n\n      const Number alpha_hat_max = c(gamma_max) * alpha_max;\n\n      const Number gamma_m = std::min(gamma_i, gamma_j);\n      const Number gamma_M = std::max(gamma_i, gamma_j);\n\n      const Number p_ratio = safe_division(p_min, p_max);\n\n      /*\n       * Here, we use a trick: The r-factor only shows up in the formula\n       * for the case \\gamma_min = \\gamma_m, otherwise the r-factor\n       * vanishes. We can accomplish this by using the following modified\n       * exponent (where we substitute gamma_m by gamma_min):\n       */\n      const Number r_exponent =\n          (gamma_M - gamma_min) / (ScalarNumber(2.) * gamma_min * gamma_M);\n\n      /*\n       * Compute a simultaneous upper bound on\n       *   (5.7) second formula for \\tilde p_2^\\ast\n       *   (5.8) first formula for \\tilde p_1^\\ast\n       *   (5.11) formula for \\tilde p_2^\\ast\n       */\n\n      const Number exponent =\n          (gamma_m - Number(1.)) / (ScalarNumber(2.) * gamma_m);\n      const Number exponent_inverse = Number(1.) / exponent;\n\n      const Number numerator =\n          positive_part(alpha_hat_min + /*SIC!*/ alpha_max - (u_j - u_i));\n\n      Number denominator = alpha_hat_min * ryujin::pow(p_ratio, -exponent) +\n                           alpha_hat_max * ryujin::pow(p_ratio, r_exponent);\n\n      const auto temp = safe_division(numerator, denominator);\n\n      const Number p_tilde = p_max * ryujin::pow(temp, exponent_inverse) - pinf;\n\n#ifdef DEBUG_RIEMANN_SOLVER\n      std::cout << \"IN p_*_tilde  = \" << p_tilde << \"\\n\";\n#endif\n\n      return p_tilde;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::f(const primitive_type &riemann_data,\n                                  const Number p_star) const\n    {\n      constexpr ScalarNumber min = std::numeric_limits<ScalarNumber>::min();\n\n      const auto view = hyperbolic_system.view<dim, Number>();\n      const auto interpolation_b = view.eos_interpolation_b();\n      const auto pinf = view.eos_interpolation_pinfty();\n\n      const auto &[rho, u, p, gamma, a] = riemann_data;\n\n      const Number one_minus_b_rho = Number(1.) - interpolation_b * rho;\n      const Number gamma_minus_one = gamma - Number(1.);\n\n      const Number Az =\n          ScalarNumber(2.) * one_minus_b_rho / (rho * (gamma + Number(1.)));\n\n      const Number Bz = gamma_minus_one / (gamma + Number(1.)) * (p + pinf);\n\n      const Number radicand = safe_division(Az, p_star + pinf + Bz);\n\n      /* true_value is shock case */\n      const Number true_value = (p_star - p) * std::sqrt(radicand);\n\n      const auto exponent = ScalarNumber(0.5) * gamma_minus_one / gamma;\n\n      const Number ratio = safe_division(p_star + pinf, p + pinf);\n      const Number factor = ryujin::pow(ratio, exponent) - Number(1.);\n\n      /* false_value is rarefaction case */\n      const auto false_value = ScalarNumber(2.) * a * one_minus_b_rho * factor /\n                               std::max(gamma_minus_one, Number(min));\n\n      return dealii::compare_and_apply_mask<\n          dealii::SIMDComparison::greater_than_or_equal>(\n          p_star, p, true_value, false_value);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::phi(const primitive_type &riemann_data_i,\n                                    const primitive_type &riemann_data_j,\n                                    const Number p_in) const\n    {\n      const Number &u_i = riemann_data_i[1];\n      const Number &u_j = riemann_data_j[1];\n\n      return f(riemann_data_i, p_in) + f(riemann_data_j, p_in) + u_j - u_i;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::phi_of_p_max(\n        const primitive_type &riemann_data_i,\n        const primitive_type &riemann_data_j) const\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n      const auto interpolation_b = view.eos_interpolation_b();\n      const auto pinf = view.eos_interpolation_pinfty();\n\n      const auto &[rho_i, u_i, p_i, gamma_i, a_i] = riemann_data_i;\n      const auto &[rho_j, u_j, p_j, gamma_j, a_j] = riemann_data_j;\n\n      const Number p_max = std::max(p_i, p_j) + pinf;\n\n      const Number radicand_inverse_i =\n          safe_division(ScalarNumber(0.5) * rho_i,\n                        Number(1.) - interpolation_b * rho_i) *\n          ((gamma_i + Number(1.)) * p_max +\n           (gamma_i - Number(1.)) * (p_i + pinf));\n\n      const Number value_i =\n          safe_division(p_max - p_i, std::sqrt(radicand_inverse_i));\n\n      const Number radicand_inverse_j =\n          safe_division(ScalarNumber(0.5) * rho_j,\n                        Number(1.) - interpolation_b * rho_j) *\n          ((gamma_j + Number(1.)) * p_max +\n           (gamma_j - Number(1.)) * (p_j + pinf));\n\n      const Number value_j =\n          safe_division(p_max - p_j, std::sqrt(radicand_inverse_j));\n\n      return value_i + value_j + u_j - u_i;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::lambda1_minus(\n        const primitive_type &riemann_data, const Number p_star) const\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n      const auto pinf = view.eos_interpolation_pinfty();\n\n      const auto &[rho, u, p, gamma, a] = riemann_data;\n\n      const auto factor =\n          ScalarNumber(0.5) * (gamma + ScalarNumber(1.)) / gamma;\n\n      const Number tmp = safe_division(positive_part(p_star - p), p + pinf);\n\n      return u - a * std::sqrt(Number(1.) + factor * tmp);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::lambda3_plus(const primitive_type &riemann_data,\n                                             const Number p_star) const\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n      const auto pinf = view.eos_interpolation_pinfty();\n\n      const auto &[rho, u, p, gamma, a] = riemann_data;\n\n      const auto factor =\n          ScalarNumber(0.5) * (gamma + ScalarNumber(1.)) / gamma;\n\n      const Number tmp = safe_division(positive_part(p_star - p), p + pinf);\n\n      return u + a * std::sqrt(Number(1.) + factor * tmp);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::compute_lambda(\n        const primitive_type &riemann_data_i,\n        const primitive_type &riemann_data_j,\n        const Number p_star) const\n    {\n      const Number nu_11 = lambda1_minus(riemann_data_i, p_star);\n      const Number nu_32 = lambda3_plus(riemann_data_j, p_star);\n\n      return std::max(positive_part(nu_32), negative_part(nu_11));\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    RiemannSolver<dim, Number>::riemann_data_from_state(\n        const state_type &U,\n        const Number &p,\n        const dealii::Tensor<1, dim, Number> &n_ij) const -> primitive_type\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      const auto rho = view.density(U);\n      const auto rho_inverse = ScalarNumber(1.0) / rho;\n\n      const auto m = view.momentum(U);\n      const auto proj_m = n_ij * m;\n\n      const auto gamma = view.surrogate_gamma(U, p);\n\n      const auto interpolation_b = view.eos_interpolation_b();\n      const auto pinf = view.eos_interpolation_pinfty();\n      const auto x = Number(1.) - interpolation_b * rho;\n      const auto a = std::sqrt(gamma * (p + pinf) / (rho * x));\n\n#ifdef DEBUG_EXPENSIVE_BOUNDS_CHECK\n      AssertThrowSIMD(\n          Number(p + pinf),\n          [](auto val) { return val >= ScalarNumber(0.); },\n          dealii::ExcMessage(\"Internal error: p + pinf < 0.\"));\n\n      AssertThrowSIMD(\n          x,\n          [](auto val) { return val > ScalarNumber(0.); },\n          dealii::ExcMessage(\"Internal error: 1. - b * rho <= 0.\"));\n\n      AssertThrowSIMD(\n          gamma,\n          [](auto val) { return val >= ScalarNumber(1.); },\n          dealii::ExcMessage(\"Internal error: gamma < 1.\"));\n#endif\n\n      return {{rho, proj_m * rho_inverse, p, gamma, a}};\n    }\n\n\n    template <int dim, typename Number>\n    Number RiemannSolver<dim, Number>::compute(\n        const primitive_type &riemann_data_i,\n        const primitive_type &riemann_data_j) const\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n      const auto pinf = view.eos_interpolation_pinfty();\n\n      const auto &[rho_i, u_i, p_i, gamma_i, a_i] = riemann_data_i;\n      const auto &[rho_j, u_j, p_j, gamma_j, a_j] = riemann_data_j;\n\n#ifdef DEBUG_RIEMANN_SOLVER\n      std::cout << \"rho_left: \" << rho_i << std::endl;\n      std::cout << \"u_left: \" << u_i << std::endl;\n      std::cout << \"p_left: \" << p_i << std::endl;\n      std::cout << \"gamma_left: \" << gamma_i << std::endl;\n      std::cout << \"a_left: \" << a_i << std::endl;\n      std::cout << \"rho_right: \" << rho_j << std::endl;\n      std::cout << \"u_right: \" << u_j << std::endl;\n      std::cout << \"p_right: \" << p_j << std::endl;\n      std::cout << \"gamma_right: \" << gamma_j << std::endl;\n      std::cout << \"a_right: \" << a_j << std::endl;\n#endif\n\n      const Number p_max = std::max(p_i, p_j) + pinf;\n      const Number phi_p_max = phi_of_p_max(riemann_data_i, riemann_data_j);\n\n      if (!view.compute_strict_bounds()) {\n#ifdef DEBUG_RIEMANN_SOLVER\n        const Number p_star_RS = p_star_RS_full(riemann_data_i, riemann_data_j);\n        const Number p_star_SS = p_star_SS_full(riemann_data_i, riemann_data_j);\n        const Number p_debug =\n            dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(\n                phi_p_max, Number(0.), p_star_SS, std::min(p_max, p_star_RS));\n        std::cout << \"   p^*_debug  = \" << p_debug << \"\\n\";\n        std::cout << \"   phi(p_*_d) = \"\n                  << phi(riemann_data_i, riemann_data_j, p_debug) << \"\\n\";\n        std::cout << \"-> lambda_deb = \"\n                  << compute_lambda(riemann_data_i, riemann_data_j, p_debug)\n                  << std::endl;\n#endif\n\n        const Number p_star_tilde =\n            p_star_interpolated(riemann_data_i, riemann_data_j);\n        const Number p_star_backup =\n            p_star_failsafe(riemann_data_i, riemann_data_j);\n\n        const Number p_2 =\n            dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(\n                phi_p_max,\n                Number(0.),\n                std::min(p_star_tilde, p_star_backup),\n                std::min(p_max, p_star_tilde));\n\n#ifdef DEBUG_RIEMANN_SOLVER\n        std::cout << \"   p^*_tilde  = \" << p_2 << \"\\n\";\n        std::cout << \"   phi(p_*_t) = \"\n                  << phi(riemann_data_i, riemann_data_j, p_2) << \"\\n\";\n        std::cout << \"-> lambda_max = \"\n                  << compute_lambda(riemann_data_i, riemann_data_j, p_2) << \"\\n\"\n                  << std::endl;\n#endif\n\n        return compute_lambda(riemann_data_i, riemann_data_j, p_2);\n      }\n\n      const Number p_star_RS = p_star_RS_full(riemann_data_i, riemann_data_j);\n      const Number p_star_SS = p_star_SS_full(riemann_data_i, riemann_data_j);\n\n      const Number p_2 =\n          dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(\n              phi_p_max, Number(0.), p_star_SS, std::min(p_max, p_star_RS));\n\n#ifdef DEBUG_RIEMANN_SOLVER\n      std::cout << \"   p^*_tilde  = \" << p_2 << \"\\n\";\n      std::cout << \"   phi(p_*_t) = \"\n                << phi(riemann_data_i, riemann_data_j, p_2) << \"\\n\";\n      std::cout << \"-> lambda_max = \"\n                << compute_lambda(riemann_data_i, riemann_data_j, p_2)\n                << std::endl;\n#endif\n\n      return compute_lambda(riemann_data_i, riemann_data_j, p_2);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number RiemannSolver<dim, Number>::compute(\n        const state_type &U_i,\n        const state_type &U_j,\n        const unsigned int i,\n        const unsigned int *js,\n        const dealii::Tensor<1, dim, Number> &n_ij) const\n    {\n      const auto &[p_i, unused_i, s_i, eta_i] =\n          precomputed_values.template read_tensor<Number, precomputed_type>(i);\n\n      const auto &[p_j, unused_j, s_j, eta_j] =\n          precomputed_values.template read_tensor<Number, precomputed_type>(js);\n\n      const auto riemann_data_i = riemann_data_from_state(U_i, p_i, n_ij);\n      const auto riemann_data_j = riemann_data_from_state(U_j, p_j, n_ij);\n\n      return compute(riemann_data_i, riemann_data_j);\n    }\n\n\n  } // namespace EulerAEOS\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_barotropic/CMakeLists.txt",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2025 by the ryujin authors\n##\n\nadd_library(obj_euler_barotropic OBJECT\n  equation_dispatch.cc\n  barotropic_equation_of_state_library.cc\n  initial_state_library.cc\n  limiter.cc\n  riemann_solver.cc\n  )\nset_target_properties(obj_euler_barotropic PROPERTIES LINKER_LANGUAGE CXX)\ndeal_ii_setup_target(obj_euler_barotropic)\ntarget_link_libraries(obj_euler_barotropic obj_common ${EXTERNAL_TARGETS})\n# Propagate the current source directory with PUBLIC visibility\ntarget_include_directories(obj_euler_barotropic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})\n"
  },
  {
    "path": "source/euler_barotropic/Makefile",
    "content": "default: all\n.PHONY: default\n\n%:\n\t@cd .. && make $@\n.PHONY: %\n"
  },
  {
    "path": "source/euler_barotropic/barotropic_equation_of_state.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"convenience_macros.h\"\n\n#include <deal.II/base/array_view.h>\n#include <deal.II/base/exceptions.h>\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/tensor.h>\n\n#include <string>\n\nnamespace ryujin\n{\n  namespace BarotropicEquationOfStateLibrary\n  {\n    /**\n     * A small abstract base class to group configuration options for a\n     * barotropic equation of state.\n     *\n     * @ingroup EulerEquations\n     */\n    class BarotropicEquationOfState : public dealii::ParameterAcceptor\n    {\n    public:\n      /**\n       * Constructor taking EOS name @p name and a subsection @p subsection\n       * as an argument. The dealii::ParameterAcceptor is initialized with\n       * the subsubsection `subsection + \"/\" + name`.\n       */\n      BarotropicEquationOfState(const std::string &name,\n                                const std::string &subsection)\n          : ParameterAcceptor(subsection + \"/\" + name)\n          , name_(name)\n      {\n      }\n\n      /**\n       * Return the specific internal energy @p e for a given density @p rho.\n       */\n      virtual double specific_internal_energy(double rho) const = 0;\n\n      /**\n       * Return the pressure for a given density @p rho.\n       */\n      virtual double pressure(double rho) const = 0;\n\n      /**\n       * Return the sound speed @p c for a given density @p rho.\n       */\n      virtual double speed_of_sound(double rho) const = 0;\n\n      /**\n       * Return the name of the EOS as (const reference) std::string\n       */\n      ACCESSOR_READ_ONLY(name)\n\n    private:\n      const std::string name_;\n    };\n\n  } // namespace BarotropicEquationOfStateLibrary\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/euler_barotropic/barotropic_equation_of_state_function.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2025 - 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"barotropic_equation_of_state.h\"\n\n#include <deal.II/base/function_parser.h>\n\nnamespace ryujin\n{\n  namespace BarotropicEquationOfStateLibrary\n  {\n    /**\n     * A user-specified equation of state for the barotropic Euler equations.\n     *\n     * The specific internal energy, pressure and speed of sound have to\n     * satisfy the following relationships:\n     *\n     * \\f{align}\n     *   p &= \\rho^2 \\,\\partial_\\rho e(\\rho), \\qquad\n     *   a &= \\sqrt{\\partial_\\rho p(\\rho)}.\n     * \\f}\n     *\n     * @ingroup EulerEquations\n     */\n    class Function : public BarotropicEquationOfState\n    {\n    public:\n      Function(const std::string &subsection)\n          : BarotropicEquationOfState(\"function\", subsection)\n      {\n        sie_expression_ = \"4. * ln(rho)\";\n        add_parameter(\"specific internal energy\",\n                      sie_expression_,\n                      \"A function expression for the specific internal energy \"\n                      \"as a function of density: e(rho)\");\n\n        p_expression_ = \"4. * rho\";\n        add_parameter(\"pressure\",\n                      p_expression_,\n                      \"A function expression for the pressure as a function of \"\n                      \"density: p(rho)\");\n\n        sos_expression_ = \"2.\";\n        add_parameter(\"speed of sound\",\n                      sos_expression_,\n                      \"A function expression for the speed of sound as a \"\n                      \"function of density: a(rho)\");\n\n        /*\n         * Set up the muparser object with the final equation of state\n         * description from the parameter file:\n         */\n        const auto set_up_muparser = [this] {\n          sie_function_ = std::make_unique<dealii::FunctionParser<1>>();\n          sie_function_->initialize(\"rho\", sie_expression_, {});\n\n          p_function_ = std::make_unique<dealii::FunctionParser<1>>();\n          p_function_->initialize(\"rho\", p_expression_, {});\n\n          sos_function_ = std::make_unique<dealii::FunctionParser<1>>();\n          sos_function_->initialize(\"rho\", sos_expression_, {});\n        };\n\n        set_up_muparser();\n        ParameterAcceptor::parse_parameters_call_back.connect(set_up_muparser);\n      }\n\n      double specific_internal_energy(double rho) const final\n      {\n        return sie_function_->value(dealii::Point<1>(rho));\n      }\n\n      double pressure(double rho) const final\n      {\n        return p_function_->value(dealii::Point<1>(rho));\n      }\n\n      double speed_of_sound(double rho) const final\n      {\n        return sos_function_->value(dealii::Point<1>(rho));\n      }\n\n    private:\n      std::string sie_expression_;\n      std::string p_expression_;\n      std::string sos_expression_;\n\n      std::unique_ptr<dealii::FunctionParser<1>> sie_function_;\n      std::unique_ptr<dealii::FunctionParser<1>> p_function_;\n      std::unique_ptr<dealii::FunctionParser<1>> sos_function_;\n    };\n  } // namespace BarotropicEquationOfStateLibrary\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_barotropic/barotropic_equation_of_state_isentropic.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"barotropic_equation_of_state.h\"\n\nnamespace ryujin\n{\n  namespace BarotropicEquationOfStateLibrary\n  {\n    /**\n     * Isentropic equation of state for the barotropic Euler equations. The\n     * specific internal energy, pressure, and speed of sound are given by:\n     *\n     * \\f{align}\n     *   e &= \\frac{k}{\\gamma-1}\\,\\rho^{\\gamma - 1}, \\qquad\n     *   p &= k\\,\\rho^{\\gamma}, \\qquad\n     *   a^2 &= \\gamma\\,k\\,\\rho^{\\gamma - 1}.\n     * \\f}\n     *\n     * @ingroup EulerEquations\n     */\n    class Isentropic : public BarotropicEquationOfState\n    {\n    public:\n      Isentropic(const std::string &subsection)\n          : BarotropicEquationOfState(\"isentropic\", subsection)\n      {\n        k_ = 1.;\n        this->add_parameter(\"k\", k_, \"Scaling factor k\");\n\n        gamma_ = 7. / 5.;\n        this->add_parameter(\"gamma\", gamma_, \"The ratio of specific heats\");\n      }\n\n      double specific_internal_energy(double rho) const final\n      {\n        return k_ / (gamma_ - 1) * std::pow(rho, gamma_ - 1.);\n      }\n\n      double pressure(double rho) const final\n      {\n        return k_ * std::pow(rho, gamma_);\n      }\n\n      double speed_of_sound(double rho) const final\n      {\n        return std::sqrt(gamma_ * k_ * std::pow(rho, gamma_ - 1.));\n      }\n\n    private:\n      double k_;\n      double gamma_;\n    };\n  } // namespace BarotropicEquationOfStateLibrary\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_barotropic/barotropic_equation_of_state_isothermal.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"barotropic_equation_of_state.h\"\n\nnamespace ryujin\n{\n  namespace BarotropicEquationOfStateLibrary\n  {\n    /**\n     * Isothermal equation of state for the barotropic Euler equations. The\n     * specific internal energy, pressure, and speed of sound are given by:\n     *\n     * \\f{align}\n     *   e &= c^2 \\,\\log(\\rho), \\qquad\n     *   p &= c^2 \\,\\rho, \\qquad\n     *   a^2 &= c^2.\n     * \\f}\n     *\n     * @ingroup EulerEquations\n     */\n    class Isothermal : public BarotropicEquationOfState\n    {\n    public:\n      Isothermal(const std::string &subsection)\n          : BarotropicEquationOfState(\"isothermal\", subsection)\n      {\n        speed_of_sound_ = 2.;\n        add_parameter(\"speed of sound\",\n                      speed_of_sound_,\n                      \"The speed of sound of the isothermal equation of state\");\n      }\n\n      double specific_internal_energy(double rho) const final\n      {\n        return speed_of_sound_ * speed_of_sound_ * std::log(rho);\n      }\n\n      double pressure(double rho) const final\n      {\n        return speed_of_sound_ * speed_of_sound_ * rho;\n      }\n\n      double speed_of_sound(double /*rho*/) const final\n      {\n        return speed_of_sound_;\n      }\n\n    private:\n      double speed_of_sound_;\n    };\n  } // namespace BarotropicEquationOfStateLibrary\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_barotropic/barotropic_equation_of_state_library.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2025 by the ryujin authors\n//\n\n#include \"barotropic_equation_of_state_library.h\"\n\n#include \"barotropic_equation_of_state_function.h\"\n#include \"barotropic_equation_of_state_isentropic.h\"\n#include \"barotropic_equation_of_state_isothermal.h\"\n#include \"barotropic_equation_of_state_pressureless.h\"\n\nnamespace ryujin\n{\n  namespace BarotropicEquationOfStateLibrary\n  {\n    /**\n     * Populate a given container with all equation of states defined in\n     * this namespace.\n     *\n     * @ingroup EulerEquations\n     */\n\n    void populate_equation_of_state_list(\n        equation_of_state_list_type &equation_of_state_list,\n        const std::string &subsection)\n    {\n      auto add = [&](auto &&object) {\n        equation_of_state_list.emplace(std::move(object));\n      };\n\n      add(std::make_shared<Function>(subsection));\n      add(std::make_shared<Isentropic>(subsection));\n      add(std::make_shared<Isothermal>(subsection));\n      add(std::make_shared<Pressureless>(subsection));\n    }\n  } // namespace BarotropicEquationOfStateLibrary\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_barotropic/barotropic_equation_of_state_library.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"barotropic_equation_of_state.h\"\n\nnamespace ryujin\n{\n  namespace BarotropicEquationOfStateLibrary\n  {\n    using equation_of_state_list_type =\n        std::set<std::shared_ptr<BarotropicEquationOfState>>;\n\n    /**\n     * Populate a given container with all equation of states defined in\n     * this namespace.\n     *\n     * @ingroup EulerEquations\n     */\n    void populate_equation_of_state_list(\n        equation_of_state_list_type &equation_of_state_list,\n        const std::string &subsection);\n\n  } // namespace BarotropicEquationOfStateLibrary\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_barotropic/barotropic_equation_of_state_pressureless.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"barotropic_equation_of_state.h\"\n\nnamespace ryujin\n{\n  namespace BarotropicEquationOfStateLibrary\n  {\n    /**\n     * Trivial equation of state for pressureless Euler\n     *\n     * @ingroup EulerEquations\n     */\n    class Pressureless : public BarotropicEquationOfState\n    {\n    public:\n      Pressureless(const std::string &subsection)\n          : BarotropicEquationOfState(\"pressureless\", subsection)\n      {\n      }\n\n      double pressure(double /*rho*/) const final\n      {\n        return 0.;\n      }\n\n      double specific_internal_energy(double /*rho*/) const final\n      {\n        return 0.;\n      }\n\n      double speed_of_sound(double /*rho*/) const final\n      {\n        return 0.;\n      }\n    };\n  } // namespace BarotropicEquationOfStateLibrary\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_barotropic/description.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"../stub_parabolic_module.h\"\n#include \"../stub_parabolic_system.h\"\n#include \"hyperbolic_system.h\"\n#include \"indicator.h\"\n#include \"limiter.h\"\n#include \"riemann_solver.h\"\n\nnamespace ryujin\n{\n  namespace EulerBarotropic\n  {\n    /**\n     * A struct that contains all equation specific classes describing the\n     * chosen hyperbolic system, the indicator, the limiter and\n     * (approximate) Riemann solver.\n     *\n     * The compressible Euler equations of gas dynamics. Specialized\n     * implementation for a subclass of barotropic equations of state where\n     * the pressure, internal energy and entropies are a function of the\n     * density. We use a specialied Riemann solver, entropy viscosity\n     * commutator, and limiter for this class of equations.\n     *\n     * The parabolic subsystem is chosen to be the identity.\n     *\n     * @ingroup EulerEquations\n     */\n    struct Description {\n      using HyperbolicSystem = EulerBarotropic::HyperbolicSystem;\n\n      template <int dim, typename Number = double>\n      using HyperbolicSystemView =\n          EulerBarotropic::HyperbolicSystemView<dim, Number>;\n\n      using ParabolicSystem = ryujin::StubParabolicSystem;\n\n      template <int dim, typename Number = double>\n      using ParabolicModule =\n          ryujin::StubParabolicModule<Description, dim, Number>;\n\n      template <int dim, typename Number = double>\n      using Indicator = EulerBarotropic::Indicator<dim, Number>;\n\n      template <int dim, typename Number = double>\n      using Limiter = EulerBarotropic::Limiter<dim, Number>;\n\n      template <int dim, typename Number = double>\n      using RiemannSolver = EulerBarotropic::RiemannSolver<dim, Number>;\n    };\n  } // namespace EulerBarotropic\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_barotropic/equation_dispatch.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2024 - 2025 by the ryujin authors\n//\n\n#include \"description.h\"\n\n#include <compile_time_options.h>\n#include <equation_dispatch.h>\n\nnamespace ryujin\n{\n  namespace EulerBarotropic\n  {\n    Dispatch<Description, NUMBER> dispatch_instance(\"euler barotropic\");\n  } // namespace EulerBarotropic\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_barotropic/hyperbolic_system.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"barotropic_equation_of_state_library.h\"\n\n#include <convenience_macros.h>\n#include <discretization.h>\n#include <loop.h>\n#include <multicomponent_vector.h>\n#include <patterns_conversion.h>\n#include <simd.h>\n#include <state_vector.h>\n\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/tensor.h>\n\n#include <array>\n\nnamespace ryujin\n{\n  namespace EulerBarotropic\n  {\n    /*\n     * For various divisions in the barotropic equation of state module we\n     * have a mathematical guarantee that the numerator and denominator are\n     * nonnegative and the limit (of zero numerator and denominator) must\n     * converge to zero. The following function takes care of rounding\n     * issues when computing such quotients by (a) avoiding division by\n     * zero and (b) ensuring non-negativity of the result.\n     */\n    template <typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number safe_division(const Number &numerator,\n                                                      const Number &denominator)\n    {\n      using ScalarNumber = typename get_value_type<Number>::type;\n      constexpr ScalarNumber min = std::numeric_limits<ScalarNumber>::min();\n\n      return std::max(numerator, Number(0.)) /\n             std::max(denominator, Number(min));\n    }\n\n\n    template <int dim, typename Number>\n    class HyperbolicSystemView;\n\n    /**\n     * The compressible Euler equations of gas dynamics. Specialized\n     * implementation for a subclass of barotropic equations of state where\n     * the pressure, internal energy and entropies are a function of the\n     * density. We use a specialied Riemann solver, entropy viscosity\n     * commutator, and limiter for this class of equations.\n     *\n     * We have a (1 + dim) dimensional state space \\f$[\\rho, \\textbf m]\\f$,\n     * where \\f$\\rho\\f$ denotes the density, \\f$\\textbf m\\f$ is the\n     * momentum.\n     *\n     * @ingroup EulerEquations\n     */\n    class HyperbolicSystem final : public dealii::ParameterAcceptor\n    {\n    public:\n      /**\n       * The name of the hyperbolic system as a string.\n       */\n      static inline std::string problem_name =\n          \"Compressible Euler equations (barotropic EOS, optimized barotropic)\";\n\n      /**\n       * Constructor.\n       */\n      HyperbolicSystem(const std::string &subsection = \"/HyperbolicSystem\");\n\n      /**\n       * Return a view on the Hyperbolic System for a given dimension @p\n       * dim and choice of number type @p Number (which can be a scalar\n       * float, or double, as well as a VectorizedArray holding packed\n       * scalars.\n       */\n      template <int dim, typename Number>\n      auto view() const\n      {\n        return HyperbolicSystemView<dim, Number>{*this};\n      }\n\n      /**\n       * Part of step 1 of the hyperbolic update step: Compute \"precomputed\n       * values\" and fill into the state vector.\n       *\n       * @note The method does not update the ghost range of the state\n       * vector. The precomputed part has to be synchronized by explicitly\n       * calling the update ghost values function.\n       */\n      template <int dim, typename ScalarNumber>\n      void fill_precomputed_values(\n          const OfflineData<dim, ScalarNumber> &offline_data,\n          typename HyperbolicSystemView<dim, ScalarNumber>::StateVector\n              &state_vector,\n          const bool skip_constrained_dofs = true) const;\n\n    private:\n      /**\n       * @name Runtime parameters, internal fields, methods, and friends\n       */\n      //@{\n\n      std::string barotropic_equation_of_state_;\n      double reference_density_;\n      double vacuum_state_relaxation_small_;\n      double vacuum_state_relaxation_large_;\n\n      BarotropicEquationOfStateLibrary::equation_of_state_list_type\n          barotropic_equation_of_state_list_;\n\n      using BarotropicEquationOfState =\n          BarotropicEquationOfStateLibrary::BarotropicEquationOfState;\n      std::shared_ptr<BarotropicEquationOfState>\n          selected_barotropic_equation_of_state_;\n\n      template <int dim, typename Number>\n      friend class HyperbolicSystemView;\n      //@}\n    }; /* HyperbolicSystem */\n\n\n    /**\n     * A view of the HyperbolicSystem that makes methods available for a\n     * given dimension @p dim and choice of number type @p Number (which\n     * can be a scalar float, or double, as well as a VectorizedArray\n     * holding packed scalars.\n     *\n     * Intended usage:\n     * ```\n     * HyperbolicSystem hyperbolic_system;\n     * const auto view = hyperbolic_system.template view<dim, Number>();\n     * const auto flux_i = view.flux_contribution(...);\n     * const auto flux_j = view.flux_contribution(...);\n     * const auto flux_ij = view.flux_divergence(flux_i, flux_j, c_ij);\n     * // etc.\n     * ```\n     */\n    template <int dim, typename Number>\n    class HyperbolicSystemView\n    {\n    public:\n      /**\n       * Constructor taking a reference to the underlying\n       * HyperbolicSystem\n       */\n      HyperbolicSystemView(const HyperbolicSystem &hyperbolic_system)\n          : hyperbolic_system_(hyperbolic_system)\n      {\n      }\n\n      /**\n       * Create a modified view from the current one:\n       */\n      template <int dim2, typename Number2>\n      auto view() const\n      {\n        return HyperbolicSystemView<dim2, Number2>{hyperbolic_system_};\n      }\n\n      /**\n       * The underlying scalar number type.\n       */\n      using ScalarNumber = typename get_value_type<Number>::type;\n\n      /**\n       * @name Access to runtime parameters\n       */\n      //@{\n\n      DEAL_II_ALWAYS_INLINE inline const std::string &\n      barotropic_equation_of_state() const\n      {\n        return hyperbolic_system_.barotropic_equation_of_state_;\n      }\n\n      DEAL_II_ALWAYS_INLINE inline ScalarNumber reference_density() const\n      {\n        return hyperbolic_system_.reference_density_;\n      }\n\n      DEAL_II_ALWAYS_INLINE inline ScalarNumber\n      vacuum_state_relaxation_small() const\n      {\n        return hyperbolic_system_.vacuum_state_relaxation_small_;\n      }\n\n      DEAL_II_ALWAYS_INLINE inline ScalarNumber\n      vacuum_state_relaxation_large() const\n      {\n        return hyperbolic_system_.vacuum_state_relaxation_large_;\n      }\n\n      //@}\n      /**\n       * @name Low-level access to the selected equation of state.\n       */\n      //@{\n\n      /**\n       * For a given density \\f$\\rho\\f$ return the\n       * <i>specific</i> internal energy \\f$e\\f$.\n       */\n      DEAL_II_ALWAYS_INLINE inline Number\n      beos_specific_internal_energy(const Number &rho) const\n      {\n        const auto &beos =\n            hyperbolic_system_.selected_barotropic_equation_of_state_;\n\n        if constexpr (std::is_same_v<ScalarNumber, Number>) {\n          return ScalarNumber(beos->specific_internal_energy(rho));\n        } else {\n          Number e;\n          for (unsigned int k = 0; k < Number::size(); ++k) {\n            e[k] = ScalarNumber(beos->specific_internal_energy(rho[k]));\n          }\n          return e;\n        }\n      }\n\n      /**\n       * For a given density \\f$\\rho\\f$ return the pressure \\f$p\\f$.\n       */\n      DEAL_II_ALWAYS_INLINE inline Number beos_pressure(const Number &rho) const\n      {\n        const auto &beos =\n            hyperbolic_system_.selected_barotropic_equation_of_state_;\n\n        if constexpr (std::is_same_v<ScalarNumber, Number>) {\n          return ScalarNumber(beos->pressure(rho));\n        } else {\n          Number p;\n          for (unsigned int k = 0; k < Number::size(); ++k) {\n            p[k] = ScalarNumber(beos->pressure(rho[k]));\n          }\n          return p;\n        }\n      }\n\n      /**\n       * For a given density \\f$\\rho\\f$ and <i>specific</i> internal\n       * energy \\f$e\\f$ return the sound speed \\f$a\\f$.\n       */\n      DEAL_II_ALWAYS_INLINE inline Number\n      beos_speed_of_sound(const Number &rho) const\n      {\n        const auto &beos =\n            hyperbolic_system_.selected_barotropic_equation_of_state_;\n\n        if constexpr (std::is_same_v<ScalarNumber, Number>) {\n          return ScalarNumber(beos->speed_of_sound(rho));\n        } else {\n          Number a;\n          for (unsigned int k = 0; k < Number::size(); ++k) {\n            a[k] = ScalarNumber(beos->speed_of_sound(rho[k]));\n          }\n          return a;\n        }\n      }\n\n      //@}\n      /**\n       * constexpr booleans used in the EulerInitialStates namespace\n       */\n      //@{\n\n      static constexpr bool have_gamma = false;\n      static constexpr bool have_eos_interpolation_b = false;\n      static constexpr bool have_energy_equation = false;\n\n      //@}\n      /**\n       * @name Internal data\n       */\n      //@{\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n    public:\n      //@}\n      /**\n       * @name Types and constexpr constants\n       */\n      //@{\n\n      /**\n       * The dimension of the state space.\n       */\n      static constexpr unsigned int problem_dimension = 1 + dim;\n\n      /**\n       * Storage type for a (conserved) state vector \\f$\\boldsymbol U\\f$.\n       */\n      using state_type = dealii::Tensor<1, problem_dimension, Number>;\n\n      /**\n       * Storage type for the flux \\f$\\mathbf{f}\\f$.\n       */\n      using flux_type =\n          dealii::Tensor<1, problem_dimension, dealii::Tensor<1, dim, Number>>;\n\n      /**\n       * The storage type used for flux contributions.\n       */\n      using flux_contribution_type = flux_type;\n\n      /**\n       * An array holding all component names of the conserved state as a\n       * string.\n       */\n      static inline const auto component_names =\n          []() -> std::array<std::string, problem_dimension> {\n        if constexpr (dim == 1)\n          return {\"rho\", \"m\"};\n        else if constexpr (dim == 2)\n          return {\"rho\", \"m_1\", \"m_2\"};\n        else if constexpr (dim == 3)\n          return {\"rho\", \"m_1\", \"m_2\", \"m_3\"};\n        __builtin_trap();\n      }();\n\n      /**\n       * An array holding all component names of the primitive state as a\n       * string.\n       */\n      static inline const auto primitive_component_names =\n          []() -> std::array<std::string, problem_dimension> {\n        if constexpr (dim == 1)\n          return {\"rho\", \"v\"};\n        else if constexpr (dim == 2)\n          return {\"rho\", \"v_1\", \"v_2\"};\n        else if constexpr (dim == 3)\n          return {\"rho\", \"v_1\", \"v_2\", \"v_3\"};\n        __builtin_trap();\n      }();\n\n      /**\n       * The number of precomputed values.\n       */\n      static constexpr unsigned int n_precomputed_values = 3;\n\n      /**\n       * Array type used for precomputed values.\n       */\n      using precomputed_type = std::array<Number, n_precomputed_values>;\n\n      /**\n       * An array holding all component names of the precomputed values.\n       */\n      static inline const auto precomputed_names =\n          std::array<std::string, n_precomputed_values>{{\"e\", \"p\", \"a\"}};\n\n      /**\n       * The number of precomputed initial values.\n       */\n      static constexpr unsigned int n_initial_precomputed_values = 0;\n\n      /**\n       * Array type used for precomputed initial values.\n       */\n      using initial_precomputed_type =\n          std::array<Number, n_initial_precomputed_values>;\n\n      /**\n       * An array holding all component names of the precomputed values.\n       */\n      static inline const auto initial_precomputed_names =\n          std::array<std::string, n_initial_precomputed_values>{};\n\n      /**\n       * A compound state vector.\n       */\n      using StateVector = Vectors::\n          StateVector<ScalarNumber, problem_dimension, n_precomputed_values>;\n\n      /**\n       * MulticomponentVector for storing the hyperbolic state vector:\n       */\n      using HyperbolicVector =\n          Vectors::MultiComponentVector<ScalarNumber, problem_dimension>;\n\n      /**\n       * MulticomponentVector for storing a vector of precomputed states:\n       */\n      using PrecomputedVector =\n          Vectors::MultiComponentVector<ScalarNumber, n_precomputed_values>;\n\n      /**\n       * MulticomponentVector for storing a vector of precomputed initial\n       * states:\n       */\n      using InitialPrecomputedVector =\n          Vectors::MultiComponentVector<ScalarNumber,\n                                        n_initial_precomputed_values>;\n\n      //@}\n      /**\n       * @name Computing derived physical quantities\n       */\n      //@{\n\n      /**\n       * For a given (1+dim dimensional) state vector <code>U</code>, return\n       * the density <code>U[0]</code>\n       */\n      static Number density(const state_type &U);\n\n      /**\n       * Given a density @p rho this function returns 0 if the magnitude\n       * of rho is smaller or equal than relaxation_large * rho_cutoff.\n       * Otherwise rho is returned unmodified. Here, rho_cutoff is the\n       * reference density multiplied by eps.\n       */\n      Number filter_vacuum_density(const Number &rho) const;\n\n      /**\n       * For a given (1+dim dimensional) state vector <code>U</code>, return\n       * the momentum vector <code>[U[1], ..., U[1+dim]]</code>.\n       */\n      static dealii::Tensor<1, dim, Number> momentum(const state_type &U);\n\n      /**\n       * For a given (1+dim dimensional) barotropic state vector\n       * <code>U</code>, compute and return the total energy of the system.\n       * \\f[\n       *   \\eta = \\rho e(\\rho) + \\frac12\\rho&{-1}|\\vec m|^2\n       * \\f]\n       */\n      Number total_energy(const state_type &U,\n                          const Number &specific_internal_energy) const;\n\n      /**\n       * For a given (1+dim dimensional) barotropic state vector\n       * <code>U</code>, compute and return the derivative \\f$\\eta'\\f$ of\n       * the total energy.\n       */\n      state_type total_energy_derivative(const state_type &U,\n                                         const Number &specific_internal_energy,\n                                         const Number &pressure) const;\n\n      /**\n       * Returns whether the state @p U is admissible. If @p U is a\n       * vectorized state then @p U is admissible if all vectorized values\n       * are admissible.\n       */\n      bool is_admissible(const state_type &U) const;\n\n      //@}\n      /**\n       * @name Special functions for boundary states\n       */\n      //@{\n\n      /**\n       * Decomposes a given state @p U into Riemann invariants and then\n       * replaces the first or second Riemann characteristic from the one\n       * taken from @p U_bar state. Note that the @p U_bar state is just the\n       * prescribed dirichlet values.\n       */\n      template <int component>\n      state_type prescribe_riemann_characteristic(\n          const state_type &U,\n          const Number &p,\n          const state_type &U_bar,\n          const Number &p_bar,\n          const dealii::Tensor<1, dim, Number> &normal) const;\n\n      /**\n       * Apply boundary conditions.\n       *\n       * For the compressible Euler equations we have:\n       *\n       *  - Dirichlet boundary conditions by prescribing the return value of\n       *    get_dirichlet_data() as is.\n       *\n       *  - Slip boundary conditions where we remove the normal component of\n       *    the momentum.\n       *\n       *  - No slip boundary conditions where we set the momentum to 0.\n       *\n       *  - \"Dynamic boundary\" conditions that prescribe different Riemann\n       *    invariants from the return value of get_dirichlet_data()\n       *    depending on the flow state (supersonic versus subsonic, outflow\n       *    versus inflow).\n       */\n      template <typename Lambda>\n      state_type\n      apply_boundary_conditions(const dealii::types::boundary_id id,\n                                const state_type &U,\n                                const dealii::Tensor<1, dim, Number> &normal,\n                                const Lambda &get_dirichlet_data) const;\n\n      //@}\n      /**\n       * @name Flux computations\n       */\n      //@{\n\n      /**\n       * Given a state @p U and a pressure @p p compute the flux\n       * \\f[\n       * \\begin{pmatrix}\n       *   \\textbf m \\\\\n       *   \\textbf v\\otimes \\textbf m + p\\mathbb{I}_d\n       * \\end{pmatrix},\n       * \\f]\n       */\n      flux_type f(const state_type &U, const Number &p) const;\n\n      /**\n       * Given a state @p U_i and an index @p i compute flux contributions.\n       *\n       * Intended usage:\n       * ```\n       * Indicator<dim, Number> indicator;\n       * for (unsigned int i = n_internal; i < n_owned; ++i) {\n       *   // ...\n       *   const auto flux_i = flux_contribution(precomputed..., i, U_i);\n       *   for (unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {\n       *     // ...\n       *     const auto flux_j = flux_contribution(precomputed..., js, U_j);\n       *     const auto flux_ij = flux_divergence(flux_i, flux_j, c_ij);\n       *   }\n       * }\n       * ```\n       *\n       * For the Euler equations we simply compute <code>f(U_i)</code>.\n       */\n      flux_contribution_type\n      flux_contribution(const PrecomputedVector &pv,\n                        const InitialPrecomputedVector &piv,\n                        const unsigned int i,\n                        const state_type &U_i) const;\n\n      flux_contribution_type\n      flux_contribution(const PrecomputedVector &pv,\n                        const InitialPrecomputedVector &piv,\n                        const unsigned int *js,\n                        const state_type &U_j) const;\n\n      /**\n       * Given flux contributions @p flux_i and @p flux_j compute the flux\n       * <code>(-f(U_i) - f(U_j)</code>\n       */\n      state_type\n      flux_divergence(const flux_contribution_type &flux_i,\n                      const flux_contribution_type &flux_j,\n                      const dealii::Tensor<1, dim, Number> &c_ij) const;\n\n      /**\n       * The low-order and high-order fluxes are the same:\n       */\n      static constexpr bool have_high_order_flux = false;\n\n      state_type high_order_flux_divergence(\n          const flux_contribution_type &flux_i,\n          const flux_contribution_type &flux_j,\n          const dealii::Tensor<1, dim, Number> &c_ij) const = delete;\n\n      /**\n       * @name Computing stencil source terms\n       */\n      //@{\n\n      /** We do not have source terms */\n      static constexpr bool have_source_terms = false;\n\n      state_type nodal_source(const PrecomputedVector &pv,\n                              const unsigned int i,\n                              const state_type &U_i,\n                              const ScalarNumber tau) const = delete;\n\n      state_type nodal_source(const PrecomputedVector &pv,\n                              const unsigned int *js,\n                              const state_type &U_j,\n                              const ScalarNumber tau) const = delete;\n\n      //@}\n      /**\n       * @name State transformations\n       */\n      //@{\n\n      /**\n       * Given a state vector associated with a different spatial\n       * dimensions than the current one, return an \"expanded\" version of\n       * the state vector associated with @a dim spatial dimensions where\n       * the momentum vector of the conserved state @p state is expaned\n       * with zeros to a total length of @a dim entries.\n       *\n       * @note @a dim has to be larger or equal than the dimension of the\n       * @a ST vector.\n       */\n      template <typename ST>\n      state_type expand_state(const ST &state) const;\n\n      /**\n       * Given an initial state [rho, u_1, ..., u_d, p] return a\n       * conserved state [rho, m_1, ..., m_d, E]. Most notably, the\n       * specific equation of state oracle is queried to convert the\n       * pressure value into a specific internal energy.\n       *\n       * @note This function is used to conveniently convert (user\n       * provided) primitive initial states with pressure values to a\n       * conserved state in the EulerInitialStateLibrary. As such, this\n       * function is implemented in the Euler::HyperbolicSystem and\n       * EulerBarotropic::HyperbolicSystem classes.\n       */\n      template <typename ST>\n      state_type from_initial_state(const ST &initial_state) const;\n\n      /**\n       * Given a primitive state [rho, v_1, ..., v_d] return a conserved\n       * state.\n       */\n      state_type from_primitive_state(const state_type &primitive_state) const;\n\n      /**\n       * Given a conserved state return a primitive state [rho, v_1, ..., v_d]\n       */\n      state_type to_primitive_state(const state_type &state) const;\n\n      /**\n       * Transform the current state according to a given operator\n       * @p lambda acting on a @a dim dimensional momentum (or velocity)\n       * vector.\n       */\n      template <typename Lambda>\n      state_type apply_galilei_transform(const state_type &state,\n                                         const Lambda &lambda) const;\n      //@}\n    }; /* HyperbolicSystemView */\n\n\n    /*\n     * -------------------------------------------------------------------------\n     * Inline definitions\n     * -------------------------------------------------------------------------\n     */\n\n\n    inline HyperbolicSystem::HyperbolicSystem(\n        const std::string &subsection /*= \"HyperbolicSystem\"*/)\n        : ParameterAcceptor(subsection)\n    {\n      barotropic_equation_of_state_ = \"isothermal\";\n      add_parameter(\"barotropic equation of state\",\n                    barotropic_equation_of_state_,\n                    \"The barotropic equation of state. Valid names are given \"\n                    \"by any of the subsections defined below\");\n\n      reference_density_ = 1.;\n      add_parameter(\"reference density\",\n                    reference_density_,\n                    \"Problem specific density reference\");\n\n      vacuum_state_relaxation_small_ = 1.e2;\n      add_parameter(\"vacuum state relaxation small\",\n                    vacuum_state_relaxation_small_,\n                    \"Problem specific vacuum relaxation parameter\");\n\n      vacuum_state_relaxation_large_ = 1.e4;\n      add_parameter(\"vacuum state relaxation large\",\n                    vacuum_state_relaxation_large_,\n                    \"Problem specific vacuum relaxation parameter\");\n\n      /*\n       * And finally populate the equation of state list with all equation of\n       * state configurations defined in the EquationOfState namespace:\n       */\n      BarotropicEquationOfStateLibrary::populate_equation_of_state_list(\n          barotropic_equation_of_state_list_, subsection);\n\n      const auto populate_functions = [this]() {\n        bool initialized = false;\n        for (auto &it : barotropic_equation_of_state_list_)\n\n          /* Populate EOS-specific quantities and functions */\n          if (it->name() == barotropic_equation_of_state_) {\n            selected_barotropic_equation_of_state_ = it;\n            problem_name = \"Compressible Euler equations (\" + it->name() +\n                           \" EOS, optimized barotropic)\";\n            initialized = true;\n            break;\n          }\n\n        AssertThrow(initialized,\n                    dealii::ExcMessage(\"Could not find a barotropic equation \"\n                                       \"of state description with name \\\"\" +\n                                       barotropic_equation_of_state_ + \"\\\"\"));\n      };\n\n      ParameterAcceptor::parse_parameters_call_back.connect(populate_functions);\n      populate_functions();\n    }\n\n\n    template <int dim, typename ScalarNumber>\n    inline void HyperbolicSystem::fill_precomputed_values(\n        const OfflineData<dim, ScalarNumber> &offline_data,\n        typename HyperbolicSystemView<dim, ScalarNumber>::StateVector\n            &state_vector,\n        const bool skip_constrained_dofs) const\n    {\n      const unsigned int n_internal = offline_data.n_locally_internal();\n      const unsigned int n_owned = offline_data.n_locally_owned();\n      const auto &sparsity_simd = offline_data.sparsity_pattern_simd();\n      using VA = dealii::VectorizedArray<ScalarNumber>;\n\n      const auto &U = std::get<0>(state_vector);\n      auto &precomputed = std::get<1>(state_vector);\n\n      const auto body = [&](auto sentinel, unsigned int i) {\n        using T = decltype(sentinel);\n        using View = HyperbolicSystemView<dim, T>;\n        using precomputed_type = typename View::precomputed_type;\n\n        const unsigned int row_length = sparsity_simd.row_length(i);\n        if (skip_constrained_dofs && row_length == 1)\n          return;\n\n        const auto U_i = U.template read_tensor<T>(i);\n        const auto view = this->view<dim, T>();\n        const auto rho_i = view.density(U_i);\n\n        const auto e_i = view.beos_specific_internal_energy(rho_i);\n        const auto p_i = view.beos_pressure(rho_i);\n        const auto a_i = view.beos_speed_of_sound(rho_i);\n\n        const precomputed_type prec_i{e_i, p_i, a_i};\n        precomputed.template write_tensor<T>(prec_i, i);\n      };\n\n      cpu_simd_loop<ScalarNumber>(\"time_step_1\", body, 0, n_internal, n_owned);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::density(const state_type &U)\n    {\n      return U[0];\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::filter_vacuum_density(\n        const Number &rho) const\n    {\n      constexpr ScalarNumber eps = std::numeric_limits<ScalarNumber>::epsilon();\n      const Number rho_cutoff_large =\n          reference_density() * vacuum_state_relaxation_large() * eps;\n\n      return dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(\n          std::abs(rho), rho_cutoff_large, Number(0.), rho);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline dealii::Tensor<1, dim, Number>\n    HyperbolicSystemView<dim, Number>::momentum(const state_type &U)\n    {\n      dealii::Tensor<1, dim, Number> result;\n      for (unsigned int i = 0; i < dim; ++i)\n        result[i] = U[1 + i];\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::total_energy(\n        const state_type &U, const Number &specific_internal_energy) const\n    {\n      const auto rho = density(U);\n      const auto rho_inverse = ScalarNumber(1.) / rho;\n      const auto m = momentum(U);\n\n      return rho * specific_internal_energy +\n             ScalarNumber(0.5) * rho_inverse * m.norm_square();\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::total_energy_derivative(\n        const state_type &U,\n        const Number &specific_internal_energy,\n        const Number &pressure) const -> state_type\n    {\n      const auto rho = density(U);\n      const auto rho_inverse = ScalarNumber(1.) / rho;\n      const auto m = momentum(U);\n\n      state_type result;\n\n      result[0] =\n          specific_internal_energy + rho_inverse * pressure -\n          ScalarNumber(0.5) * rho_inverse * rho_inverse * m.norm_square();\n      for (unsigned int i = 0; i < dim; ++i)\n        result[1 + i] = rho_inverse * m[i];\n\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline bool\n    HyperbolicSystemView<dim, Number>::is_admissible(const state_type &U) const\n    {\n      const auto rho = density(U);\n      constexpr auto gt = dealii::SIMDComparison::greater_than;\n      using T = Number;\n      const auto test =\n          dealii::compare_and_apply_mask<gt>(rho, T(0.), T(0.), T(-1.));\n\n#ifdef DEBUG_OUTPUT\n      if (!(test == Number(0.))) {\n        std::cout << std::fixed << std::setprecision(16);\n        std::cout << \"Bounds violation: Negative state [rho, e] detected!\\n\";\n        std::cout << \"\\t\\trho:           \" << rho << \"\\n\";\n      }\n#endif\n\n      return (test == Number(0.));\n    }\n\n\n    template <int dim, typename Number>\n    template <int component>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::prescribe_riemann_characteristic(\n        const state_type & /*U*/,\n        const Number & /*p*/,\n        const state_type & /*U_bar*/,\n        const Number & /*p_bar*/,\n        const dealii::Tensor<1, dim, Number> & /*normal*/) const -> state_type\n    {\n      // FIXME\n      AssertThrow(false, dealii::ExcNotImplemented());\n      __builtin_trap();\n      return state_type{};\n    }\n\n\n    template <int dim, typename Number>\n    template <typename Lambda>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::apply_boundary_conditions(\n        dealii::types::boundary_id id,\n        const state_type &U,\n        const dealii::Tensor<1, dim, Number> &normal,\n        const Lambda &get_dirichlet_data) const -> state_type\n    {\n      state_type result = U;\n\n      if (id == Boundary::dirichlet) {\n        result = get_dirichlet_data();\n\n      } else if (id == Boundary::dirichlet_momentum) {\n        /* Only enforce Dirichlet conditions on the momentum: */\n        auto m_dirichlet = momentum(get_dirichlet_data());\n        for (unsigned int k = 0; k < dim; ++k)\n          result[k + 1] = m_dirichlet[k];\n\n      } else if (id == Boundary::dirichlet_velocity) {\n        /* Only enforce Dirichlet conditions on the velocity: */\n        const auto U_dirichlet = get_dirichlet_data();\n        const auto rho_dirichlet = density(U_dirichlet);\n        const auto v_dirichlet = momentum(U_dirichlet) / rho_dirichlet;\n        const auto rho = density(result);\n        for (unsigned int k = 0; k < dim; ++k)\n          result[k + 1] = rho * v_dirichlet[k];\n\n      } else if (id == Boundary::slip) {\n        auto m = momentum(U);\n        m -= 1. * (m * normal) * normal;\n        for (unsigned int k = 0; k < dim; ++k)\n          result[k + 1] = m[k];\n\n      } else if (id == Boundary::no_slip) {\n        for (unsigned int k = 0; k < dim; ++k)\n          result[k + 1] = Number(0.);\n\n      } else if (id == Boundary::dynamic) {\n        /*\n         * On dynamic boundary conditions, we distinguish four cases:\n         *\n         *  - supersonic inflow: prescribe full state\n         *  - subsonic inflow:\n         *      decompose into Riemann invariants and leave R_2\n         *      characteristic untouched.\n         *  - supersonic outflow: do nothing\n         *  - subsonic outflow:\n         *      decompose into Riemann invariants and prescribe incoming\n         *      R_1 characteristic.\n         */\n        const auto m = momentum(U);\n        const auto rho = density(U);\n\n        /*\n         * We do not have precomputed values available. Thus, simply query\n         * the pressure and speed of sound oracle:\n         */\n        const auto p = beos_pressure(rho);\n        const auto a = beos_speed_of_sound(rho);\n        const auto vn = m * normal / rho;\n\n        /* Supersonic inflow: */\n        if (vn < -a) {\n          result = get_dirichlet_data();\n        }\n\n        /* Subsonic inflow: */\n        if (vn >= -a && vn <= 0.) {\n          const auto U_dirichlet = get_dirichlet_data();\n          const auto rho_dirichlet = density(U_dirichlet);\n          const auto p_dirichlet = beos_pressure(rho_dirichlet);\n\n          result = prescribe_riemann_characteristic<2>(\n              U_dirichlet, p_dirichlet, U, p, normal);\n        }\n\n        /* Subsonic outflow: */\n        if (vn > 0. && vn <= a) {\n          const auto U_dirichlet = get_dirichlet_data();\n          const auto rho_dirichlet = density(U_dirichlet);\n          const auto p_dirichlet = beos_pressure(rho_dirichlet);\n\n          result = prescribe_riemann_characteristic<1>(\n              U, p, U_dirichlet, p_dirichlet, normal);\n        }\n        /* Supersonic outflow: do nothing, i.e., keep U as is */\n\n      } else {\n        AssertThrow(false, dealii::ExcNotImplemented());\n      }\n\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::f(const state_type &U,\n                                         const Number &p) const -> flux_type\n    {\n      const auto rho_inverse = ScalarNumber(1.) / density(U);\n      const auto m = momentum(U);\n\n      flux_type result;\n\n      result[0] = m;\n      for (unsigned int i = 0; i < dim; ++i) {\n        result[1 + i] = m * (m[i] * rho_inverse);\n        result[1 + i][i] += p;\n      }\n\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::flux_contribution(\n        const PrecomputedVector &pv,\n        const InitialPrecomputedVector & /*piv*/,\n        const unsigned int i,\n        const state_type &U_i) const -> flux_contribution_type\n    {\n      const auto &[e_i, p_i, a_i] =\n          pv.template read_tensor<Number, precomputed_type>(i);\n      return f(U_i, p_i);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::flux_contribution(\n        const PrecomputedVector &pv,\n        const InitialPrecomputedVector & /*piv*/,\n        const unsigned int *js,\n        const state_type &U_j) const -> flux_contribution_type\n    {\n      const auto &[e_j, p_j, a_j] =\n          pv.template read_tensor<Number, precomputed_type>(js);\n      return f(U_j, p_j);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::flux_divergence(\n        const flux_contribution_type &flux_i,\n        const flux_contribution_type &flux_j,\n        const dealii::Tensor<1, dim, Number> &c_ij) const -> state_type\n    {\n      return -contract(add(flux_i, flux_j), c_ij);\n    }\n\n\n    template <int dim, typename Number>\n    template <typename ST>\n    auto HyperbolicSystemView<dim, Number>::expand_state(const ST &state) const\n        -> state_type\n    {\n      using T = typename ST::value_type;\n      static_assert(std::is_same_v<Number, T>, \"template mismatch\");\n\n      constexpr auto dim2 = ST::dimension - 1;\n      static_assert(dim >= dim2,\n                    \"the space dimension of the argument state must not be \"\n                    \"larger than the one of the target state\");\n\n      state_type result;\n      result[0] = state[0];\n      for (unsigned int i = 1; i < dim2 + 1; ++i)\n        result[i] = state[i];\n\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    template <typename ST>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::from_initial_state(\n        const ST &initial_state) const -> state_type\n    {\n      const auto primitive_state = expand_state(initial_state);\n      return from_primitive_state(primitive_state);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::from_primitive_state(\n        const state_type &primitive_state) const -> state_type\n    {\n      const auto rho = density(primitive_state);\n\n      auto state = primitive_state;\n      /* Fix up momentum: */\n      for (unsigned int i = 1; i < dim + 1; ++i)\n        state[i] *= rho;\n\n      return state;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::to_primitive_state(\n        const state_type &state) const -> state_type\n    {\n      const auto rho = density(state);\n      const auto rho_inverse = Number(1.) / rho;\n\n      auto primitive_state = state;\n      /* Fix up velocity: */\n      for (unsigned int i = 1; i < dim + 1; ++i)\n        primitive_state[i] *= rho_inverse;\n\n      return primitive_state;\n    }\n\n\n    template <int dim, typename Number>\n    template <typename Lambda>\n    auto HyperbolicSystemView<dim, Number>::apply_galilei_transform(\n        const state_type &state, const Lambda &lambda) const -> state_type\n    {\n      auto result = state;\n      const auto M = lambda(momentum(state));\n      for (unsigned int d = 0; d < dim; ++d)\n        result[1 + d] = M[d];\n      return result;\n    }\n  } // namespace EulerBarotropic\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_barotropic/indicator.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"hyperbolic_system.h\"\n\n#include <multicomponent_vector.h>\n#include <simd.h>\n\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/vectorization.h>\n\n\nnamespace ryujin\n{\n  namespace EulerBarotropic\n  {\n    template <typename ScalarNumber = double>\n    class IndicatorParameters : public dealii::ParameterAcceptor\n    {\n    public:\n      IndicatorParameters(const std::string &subsection = \"/Indicator\")\n          : ParameterAcceptor(subsection)\n      {\n        evc_factor_ = ScalarNumber(1.);\n        add_parameter(\"evc factor\",\n                      evc_factor_,\n                      \"Factor for scaling the entropy viscocity commuator\");\n      }\n\n      ACCESSOR_READ_ONLY(evc_factor);\n\n    private:\n      ScalarNumber evc_factor_;\n    };\n\n\n    /**\n     * This class implements an indicator strategy used to form the\n     * preliminary high-order update.\n     *\n     * @ingroup EulerEquations\n     */\n    template <int dim, typename Number = double>\n    class Indicator\n    {\n    public:\n      /**\n       * @name Typedefs and constexpr constants\n       */\n      //@{\n\n      using View = HyperbolicSystemView<dim, Number>;\n\n      using ScalarNumber = typename View::ScalarNumber;\n\n      static constexpr auto problem_dimension = View::problem_dimension;\n\n      using state_type = typename View::state_type;\n\n      using flux_type = typename View::flux_type;\n\n      using precomputed_type = typename View::precomputed_type;\n\n      using PrecomputedVector = typename View::PrecomputedVector;\n\n      using Parameters = IndicatorParameters<ScalarNumber>;\n\n      //@}\n      /**\n       * @name Stencil-based computation of indicators\n       *\n       * Intended usage:\n       * ```\n       * Indicator<dim, Number> indicator;\n       * for (unsigned int i = n_internal; i < n_owned; ++i) {\n       *   // ...\n       *   indicator.reset(i, U_i);\n       *   for (unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {\n       *     // ...\n       *     indicator.accumulate(js, U_j, c_ij);\n       *   }\n       *   indicator.alpha(hd_i);\n       * }\n       * ```\n       */\n      //@{\n\n      /**\n       * Constructor taking a HyperbolicSystem instance as argument\n       */\n      Indicator(const HyperbolicSystem &hyperbolic_system,\n                const Parameters &parameters,\n                const PrecomputedVector &precomputed_values)\n          : hyperbolic_system(hyperbolic_system)\n          , parameters(parameters)\n          , precomputed_values(precomputed_values)\n      {\n      }\n\n      /**\n       * Reset temporary storage and initialize for a new row corresponding\n       * to state vector U_i.\n       */\n      void reset(const unsigned int i, const state_type &U_i);\n\n      /**\n       * When looping over the sparsity row, add the contribution associated\n       * with the neighboring state U_j.\n       */\n      void accumulate(const unsigned int *js,\n                      const state_type &U_j,\n                      const dealii::Tensor<1, dim, Number> &c_ij);\n\n      /**\n       * Return the computed alpha_i value.\n       */\n      Number alpha(const Number h_i) const;\n\n      //@}\n\n    private:\n      /**\n       * @name\n       */\n      //@{\n\n      const HyperbolicSystem &hyperbolic_system;\n      const Parameters &parameters;\n      const PrecomputedVector &precomputed_values;\n\n      Number eta_i = 0.;\n      state_type d_eta_i;\n\n      Number left = 0.;\n      state_type right;\n\n      //@}\n    };\n\n\n    /*\n     * -------------------------------------------------------------------------\n     * Inline definitions\n     * -------------------------------------------------------------------------\n     */\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline void\n    Indicator<dim, Number>::reset(const unsigned int i, const state_type &U_i)\n    {\n      /* Entropy viscosity commutator: */\n\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      const auto &[e_i, p_i, a_i] =\n          precomputed_values.template read_tensor<Number, precomputed_type>(i);\n\n      eta_i = view.total_energy(U_i, e_i);\n      d_eta_i = view.total_energy_derivative(U_i, e_i, p_i);\n\n      // left = sum_j F(U_j) * c_ij, where F is the mathematical entropy flux\n      left = 0.;\n\n      // right = sum_j f(U_j) * c_ij, where f is the flux of the system\n      right = 0.;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline void Indicator<dim, Number>::accumulate(\n        const unsigned int *js,\n        const state_type &U_j,\n        const dealii::Tensor<1, dim, Number> &c_ij)\n    {\n      /* Entropy viscosity commutator: */\n\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      const auto &[e_j, p_j, a_j] =\n          precomputed_values.template read_tensor<Number, precomputed_type>(js);\n\n      const auto rho_j = view.density(U_j);\n      const auto rho_j_inverse = Number(1.) / rho_j;\n      const auto eta_j = view.total_energy(U_j, e_j);\n\n      const auto m_j = view.momentum(U_j);\n\n      const auto f_j = view.f(U_j, p_j);\n\n      const auto entropy_flux = (eta_j + p_j) * rho_j_inverse * (m_j * c_ij);\n\n      left += entropy_flux;\n      for (unsigned int k = 0; k < problem_dimension; ++k) {\n        right[k] += f_j[k] * c_ij;\n      }\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    Indicator<dim, Number>::alpha(const Number hd_i) const\n    {\n      /* Entropy viscosity commutator: */\n\n      Number numerator = left;\n      Number denominator = std::abs(left);\n      for (unsigned int k = 0; k < problem_dimension; ++k) {\n        numerator -= d_eta_i[k] * right[k];\n        denominator += std::abs(d_eta_i[k] * right[k]);\n      }\n\n      const auto quotient = safe_division(std::abs(numerator),\n                                          denominator + hd_i * std::abs(eta_i));\n\n      return std::min(Number(1.), parameters.evc_factor() * quotient);\n    }\n  } // namespace EulerBarotropic\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_barotropic/initial_state_library.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#include \"initial_state_library.template.h\"\n\nnamespace ryujin\n{\n  template class InitialStateLibrary<Description, 1, NUMBER>;\n  template class InitialStateLibrary<Description, 2, NUMBER>;\n  template class InitialStateLibrary<Description, 3, NUMBER>;\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_barotropic/initial_state_library.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include \"description.h\"\n\n#include \"../euler/initial_state_library_euler.h\"\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  using Description = EulerBarotropic::Description;\n\n  template <int dim, typename Number>\n  class InitialStateLibrary<Description, dim, Number>\n  {\n  public:\n    using HyperbolicSystem = typename Description::HyperbolicSystem;\n    using ParabolicSystem = typename Description::ParabolicSystem;\n\n    using View =\n        typename Description::template HyperbolicSystemView<dim, Number>;\n\n    using initial_state_list_type =\n        std::set<std::unique_ptr<InitialState<Description, dim, Number>>>;\n\n    static void\n    populate_initial_state_list(initial_state_list_type &initial_state_list,\n                                const HyperbolicSystem &h,\n                                const ParabolicSystem & /*p*/,\n                                const std::string &s)\n    {\n      EulerInitialStates::populate_initial_state_list<Description, dim, Number>(\n          initial_state_list, h, s);\n    }\n  };\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_barotropic/instantiate.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#ifndef RYUJIN_INCLUDE_INSTANTIATION_ONCE\n#define RYUJIN_INCLUDE_INSTANTIATION_ONCE\n#else\n#error Instantiation files can only be included once.\n#endif\n\n#include \"description.h\"\n\nnamespace ryujin\n{\n  using EulerBarotropic::Description;\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_barotropic/limiter.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#include \"limiter.template.h\"\n\nusing namespace dealii;\n\nnamespace ryujin\n{\n  namespace EulerBarotropic\n  {\n    /* instantiations */\n\n    template class Limiter<1, NUMBER>;\n    template class Limiter<2, NUMBER>;\n    template class Limiter<3, NUMBER>;\n\n    template class Limiter<1, dealii::VectorizedArray<NUMBER>>;\n    template class Limiter<2, dealii::VectorizedArray<NUMBER>>;\n    template class Limiter<3, dealii::VectorizedArray<NUMBER>>;\n  } // namespace EulerBarotropic\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_barotropic/limiter.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"hyperbolic_system.h\"\n\n#include <multicomponent_vector.h>\n#include <newton.h>\n#include <simd.h>\n\nnamespace ryujin\n{\n  namespace EulerBarotropic\n  {\n    template <typename ScalarNumber = double>\n    class LimiterParameters : public dealii::ParameterAcceptor\n    {\n    public:\n      LimiterParameters(const std::string &subsection = \"/Limiter\")\n          : ParameterAcceptor(subsection)\n      {\n        iterations_ = 2;\n        add_parameter(\n            \"iterations\", iterations_, \"Number of limiter iterations\");\n\n        relaxation_factor_ = ScalarNumber(1.);\n        add_parameter(\"relaxation factor\",\n                      relaxation_factor_,\n                      \"Factor for scaling the relaxation window with r_i = \"\n                      \"factor * (m_i/|Omega|)^(1.5/d).\");\n      }\n\n      ACCESSOR_READ_ONLY(iterations);\n      ACCESSOR_READ_ONLY(relaxation_factor);\n\n    private:\n      unsigned int iterations_;\n      ScalarNumber relaxation_factor_;\n    };\n\n\n    /**\n     * The convex limiter.\n     *\n     * @ingroup EulerEquations\n     */\n    template <int dim, typename Number = double>\n    class Limiter\n    {\n    public:\n      /**\n       * @name Typedefs and constexpr constants\n       */\n      //@{\n\n      using View = HyperbolicSystemView<dim, Number>;\n\n      using ScalarNumber = typename View::ScalarNumber;\n\n      static constexpr auto problem_dimension = View::problem_dimension;\n\n      using state_type = typename View::state_type;\n\n      using flux_contribution_type = typename View::flux_contribution_type;\n\n      using precomputed_type = typename View::precomputed_type;\n\n      using PrecomputedVector = typename View::PrecomputedVector;\n\n      using Parameters = LimiterParameters<ScalarNumber>;\n\n      //@}\n      /**\n       * @name Computation and manipulation of bounds\n       */\n      //\n      //@{\n      /**\n       * The number of stored entries in the bounds array.\n       */\n      static constexpr unsigned int n_bounds = 2;\n\n      /**\n       * Array type used to store accumulated bounds.\n       */\n      using Bounds = std::array<Number, n_bounds>;\n\n      /**\n       * Constructor taking a HyperbolicSystem instance as argument\n       */\n      Limiter(const HyperbolicSystem &hyperbolic_system,\n              const Parameters &parameters,\n              const PrecomputedVector &precomputed_values)\n          : hyperbolic_system(hyperbolic_system)\n          , parameters(parameters)\n          , precomputed_values(precomputed_values)\n      {\n      }\n\n      /**\n       * Given a state @p U_i and an index @p i return \"strict\" bounds,\n       * i.e., a minimal convex set containing the state.\n       */\n      Bounds projection_bounds_from_state(const unsigned int i,\n                                          const state_type &U_i) const;\n\n      /**\n       * Given two bounds bounds_left, bounds_right, this function computes\n       * a larger, combined set of bounds that this is a (convex) superset\n       * of the two.\n       */\n      Bounds combine_bounds(const Bounds &bounds_left,\n                            const Bounds &bounds_right) const;\n\n      /**\n       * This function applies a relaxation to a given a (strict) bound @p\n       * bounds using a non dimensionalized measure @p hd (that should\n       * scale as $h^d$, where $h$ is the local mesh size). This is done\n       * for the case of the Euler equations by multiplying maximum bounds\n       * with $(1+r)$ and minimum bounds with $(1-r)$, while ensuring that\n       * the bounds still describe an admissible state.\n       */\n      Bounds fully_relax_bounds(const Bounds &bounds, const Number &hd) const;\n\n      //@}\n      /**\n       * @name Stencil-based computation of bounds\n       *\n       * Intended usage:\n       * ```\n       * Limiter<dim, Number> limiter;\n       * for (unsigned int i = n_internal; i < n_owned; ++i) {\n       *   // ...\n       *   limiter.reset(i, U_i, flux_i);\n       *   for (unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {\n       *     // ...\n       *     limiter.accumulate(js, U_j, flux_j, scaled_c_ij, affine_shift);\n       *   }\n       *   limiter.bounds(hd_i);\n       * }\n       * ```\n       */\n      //@{\n\n      /**\n       * Reset temporary storage\n       */\n      void reset(const unsigned int i,\n                 const state_type &U_i,\n                 const flux_contribution_type &flux_i);\n\n      /**\n       * When looping over the sparsity row, add the contribution associated\n       * with the neighboring state U_j.\n       */\n      void accumulate(const unsigned int *js,\n                      const state_type &U_j,\n                      const flux_contribution_type &flux_j,\n                      const dealii::Tensor<1, dim, Number> &scaled_c_ij,\n                      const state_type &affine_shift);\n\n      /**\n       * Return the computed bounds (with relaxation applied).\n       */\n      Bounds bounds(const Number hd_i) const;\n\n      //*}\n      /** @name Convex limiter */\n      //@{\n\n      /**\n       * Given a state \\f$\\mathbf U\\f$ and an update \\f$\\mathbf P\\f$ this\n       * function computes and returns the maximal coefficient \\f$t\\f$,\n       * obeying \\f$t_{\\text{min}} < t < t_{\\text{max}}\\f$, such that the\n       * selected local minimum principles are obeyed.\n       *\n       * The returned boolean is set to true if the original low-order\n       * update was within bounds.\n       *\n       * @note If the debug option `DEBUG_EXPENSIVE_BOUNDS_CHECK` is set to\n       * true, then the boolean is set to true if the low-order and the\n       * resulting high-order update are within bounds. The latter might be\n       * violated due to round-off errors when computing the limiter\n       * bounds.\n       */\n      std::tuple<Number, bool> limit(const Bounds &bounds,\n                                     const state_type &U,\n                                     const state_type &P,\n                                     const Number t_min = Number(0.),\n                                     const Number t_max = Number(1.)) const;\n\n    private:\n      //@}\n      /** @name Arguments and internal fields */\n      //@{\n\n      const HyperbolicSystem &hyperbolic_system;\n      const Parameters &parameters;\n      const PrecomputedVector &precomputed_values;\n\n      state_type U_i;\n      flux_contribution_type flux_i;\n\n      Bounds bounds_;\n\n      Number rho_relaxation_numerator;\n      Number rho_relaxation_denominator;\n\n      //@}\n    };\n\n\n    /*\n     * -------------------------------------------------------------------------\n     * Inline definitions\n     * -------------------------------------------------------------------------\n     */\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    Limiter<dim, Number>::projection_bounds_from_state(\n        const unsigned int /*i*/, const state_type &U_i) const -> Bounds\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n      const auto rho_i = view.density(U_i);\n      return {/*rho_min*/ rho_i, /*rho_max*/ rho_i};\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto Limiter<dim, Number>::combine_bounds(\n        const Bounds &bounds_left, const Bounds &bounds_right) const -> Bounds\n    {\n      const auto &[rho_min_l, rho_max_l] = bounds_left;\n      const auto &[rho_min_r, rho_max_r] = bounds_right;\n\n      return {std::min(rho_min_l, rho_min_r), std::max(rho_max_l, rho_max_r)};\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    Limiter<dim, Number>::fully_relax_bounds(const Bounds &bounds,\n                                             const Number &hd) const -> Bounds\n    {\n      auto relaxed_bounds = bounds;\n      auto &[rho_min_relaxed, rho_max_relaxed] = relaxed_bounds;\n\n      /* Use r = factor * (m_i / |Omega|) ^ (1.5 / d): */\n\n      Number r = std::sqrt(hd);                              // in 3D: ^ 3/6\n      if constexpr (dim == 2)                                //\n        r = dealii::Utilities::fixed_power<3>(std::sqrt(r)); // in 2D: ^ 3/4\n      else if constexpr (dim == 1)                           //\n        r = dealii::Utilities::fixed_power<3>(r);            // in 1D: ^ 3/2\n      r *= parameters.relaxation_factor();\n\n      constexpr ScalarNumber eps = std::numeric_limits<ScalarNumber>::epsilon();\n      rho_min_relaxed *= std::max(Number(1.) - r, Number(eps));\n      rho_max_relaxed *= (Number(1.) + r);\n\n      return relaxed_bounds;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline void\n    Limiter<dim, Number>::reset(const unsigned int /*i*/,\n                                const state_type &new_U_i,\n                                const flux_contribution_type &new_flux_i)\n    {\n      U_i = new_U_i;\n      flux_i = new_flux_i;\n\n      /* Bounds: */\n\n      auto &[rho_min, rho_max] = bounds_;\n\n      rho_min = Number(std::numeric_limits<ScalarNumber>::max());\n      rho_max = Number(0.);\n\n      /* Relaxation: */\n\n      rho_relaxation_numerator = Number(0.);\n      rho_relaxation_denominator = Number(0.);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline void Limiter<dim, Number>::accumulate(\n        const unsigned int * /*js*/,\n        const state_type &U_j,\n        const flux_contribution_type &flux_j,\n        const dealii::Tensor<1, dim, Number> &scaled_c_ij,\n        const state_type &affine_shift)\n    {\n      // TODO: Currently we only apply the affine_shift to U_ij_bar (which\n      // then enters all bounds), but we do not modify s_interp and\n      // rho_relaxation. When actually adding a source term to the Euler\n      // equations verify that this does the right thing.\n      Assert(std::max(affine_shift.norm(), Number(0.)) == Number(0.),\n             dealii::ExcNotImplemented());\n\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      /* Bounds: */\n      auto &[rho_min, rho_max] = bounds_;\n\n      const auto rho_i = view.density(U_i);\n      const auto rho_j = view.density(U_j);\n\n      /* bar state shifted by an affine shift: */\n      const auto U_ij_bar =\n          ScalarNumber(0.5) * (U_i + U_j) -\n          ScalarNumber(0.5) * contract(add(flux_j, -flux_i), scaled_c_ij) +\n          affine_shift;\n\n      const auto rho_ij_bar = view.density(U_ij_bar);\n\n      /* Density bounds: */\n\n      rho_min = std::min(rho_min, rho_ij_bar);\n      rho_max = std::max(rho_max, rho_ij_bar);\n\n      /* Density relaxation: */\n\n      /* Use a uniform weight. */\n      const auto beta_ij = Number(1.);\n      rho_relaxation_numerator += beta_ij * (rho_i + rho_j);\n      rho_relaxation_denominator += std::abs(beta_ij);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    Limiter<dim, Number>::bounds(const Number hd_i) const -> Bounds\n    {\n      const auto &[rho_min, rho_max] = bounds_;\n\n      auto relaxed_bounds = fully_relax_bounds(bounds_, hd_i);\n      auto &[rho_min_relaxed, rho_max_relaxed] = relaxed_bounds;\n\n      /* Apply a stricter window: */\n\n      constexpr ScalarNumber eps = std::numeric_limits<ScalarNumber>::epsilon();\n\n      const auto rho_relaxation =\n          ScalarNumber(2. * parameters.relaxation_factor()) *\n          std::abs(rho_relaxation_numerator) /\n          (std::abs(rho_relaxation_denominator) + Number(eps));\n\n      rho_min_relaxed = std::max(rho_min_relaxed, rho_min - rho_relaxation);\n      rho_max_relaxed = std::min(rho_max_relaxed, rho_max + rho_relaxation);\n\n      return relaxed_bounds;\n    }\n  } // namespace EulerBarotropic\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_barotropic/limiter.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include \"limiter.h\"\n// #define DEBUG_OUTPUT_LIMITER\n\nnamespace ryujin\n{\n  namespace EulerBarotropic\n  {\n    template <int dim, typename Number>\n    std::tuple<Number, bool>\n    Limiter<dim, Number>::limit(const Bounds &bounds,\n                                const state_type &U,\n                                const state_type &P,\n                                const Number t_min /* = Number(0.) */,\n                                const Number t_max /* = Number(1.) */) const\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      bool success = true;\n      Number t_r = t_max;\n\n      constexpr ScalarNumber eps = std::numeric_limits<ScalarNumber>::epsilon();\n      const auto large = view.vacuum_state_relaxation_large();\n      const ScalarNumber relax = ScalarNumber(1. + large * eps);\n\n      /*\n       * Limit the density rho.\n       */\n\n      {\n        const auto &rho_U = view.density(U);\n        const auto &rho_P = view.density(P);\n\n        const auto &rho_min = std::get<0>(bounds);\n        const auto &rho_max = std::get<1>(bounds);\n\n        /*\n         * Verify that rho_U is within bounds. This property might be\n         * violated for relative CFL numbers larger than 1.\n         */\n        const auto test_min = view.filter_vacuum_density(\n            std::max(Number(0.), rho_U - relax * rho_max));\n        const auto test_max = view.filter_vacuum_density(\n            std::max(Number(0.), rho_min - relax * rho_U));\n        if (!(test_min == Number(0.) && test_max == Number(0.))) {\n#ifdef DEBUG_OUTPUT\n          std::cout << std::fixed << std::setprecision(16);\n          std::cout << \"Bounds violation: low-order density (critical)!\"\n                    << \"\\n\\t\\trho min:         \" << rho_min\n                    << \"\\n\\t\\trho min (delta): \"\n                    << negative_part(rho_U - rho_min)\n                    << \"\\n\\t\\trho:             \" << rho_U\n                    << \"\\n\\t\\trho max (delta): \"\n                    << positive_part(rho_U - rho_max)\n                    << \"\\n\\t\\trho max:         \" << rho_max << \"\\n\"\n                    << std::endl;\n#endif\n          success = false;\n        }\n\n        const Number denominator =\n            ScalarNumber(1.) / (std::abs(rho_P) + eps * rho_max);\n\n        t_r = dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(\n            rho_max,\n            rho_U + t_r * rho_P,\n            /*\n             * rho_P is positive.\n             *\n             * Note: Do not take an absolute value here. If we are out of\n             * bounds we have to ensure that t_r is set to t_min.\n             */\n            (rho_max - rho_U) * denominator,\n            t_r);\n\n        t_r = dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(\n            rho_U + t_r * rho_P,\n            rho_min,\n            /*\n             * rho_P is negative.\n             *\n             * Note: Do not take an absolute value here. If we are out of\n             * bounds we have to ensure that t_r is set to t_min.\n             */\n            (rho_U - rho_min) * denominator,\n            t_r);\n\n        /*\n         * Ensure that t_min <= t <= t_max. This might not be the case if\n         * rho_U is outside the interval [rho_min, rho_max]. Furthermore,\n         * the quotient we take above is prone to numerical cancellation in\n         * particular in the second pass of the limiter when rho_P might be\n         * small.\n         */\n        t_r = std::min(t_r, t_max);\n        t_r = std::max(t_r, t_min);\n\n#ifdef DEBUG_EXPENSIVE_BOUNDS_CHECK\n        /*\n         * Verify that the new state is within bounds:\n         */\n        const auto rho_new = view.density(U + t_r * P);\n        const auto test_new_min = view.filter_vacuum_density(\n            std::max(Number(0.), rho_new - relax * rho_max));\n        const auto test_new_max = view.filter_vacuum_density(\n            std::max(Number(0.), rho_min - relax * rho_new));\n        if (!(test_new_min == Number(0.) && test_new_max == Number(0.))) {\n#ifdef DEBUG_OUTPUT\n          std::cout << std::fixed << std::setprecision(16);\n          std::cout << \"Bounds violation: high-order density!\"\n                    << \"\\n\\t\\trho min:         \" << rho_min\n                    << \"\\n\\t\\trho min (delta): \"\n                    << negative_part(rho_new - rho_min)\n                    << \"\\n\\t\\trho:             \" << rho_new\n                    << \"\\n\\t\\trho max (delta): \"\n                    << positive_part(rho_new - rho_max)\n                    << \"\\n\\t\\trho max:         \" << rho_max << \"\\n\"\n                    << std::endl;\n#endif\n          success = false;\n        }\n#endif\n      }\n\n      return {t_r, success};\n    }\n\n  } // namespace EulerBarotropic\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_barotropic/riemann_solver.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#include \"riemann_solver.template.h\"\n\n#include <deal.II/base/vectorization.h>\n\nnamespace ryujin\n{\n  namespace EulerBarotropic\n  {\n    /* instantiations */\n\n    template class RiemannSolver<1, NUMBER>;\n    template class RiemannSolver<2, NUMBER>;\n    template class RiemannSolver<3, NUMBER>;\n\n    template class RiemannSolver<1, dealii::VectorizedArray<NUMBER>>;\n    template class RiemannSolver<2, dealii::VectorizedArray<NUMBER>>;\n    template class RiemannSolver<3, dealii::VectorizedArray<NUMBER>>;\n  } // namespace EulerBarotropic\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_barotropic/riemann_solver.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"hyperbolic_system.h\"\n\n#include <simd.h>\n\n#include <deal.II/base/point.h>\n#include <deal.II/base/tensor.h>\n\nnamespace ryujin\n{\n  namespace EulerBarotropic\n  {\n    template <typename ScalarNumber = double>\n    class RiemannSolverParameters : public dealii::ParameterAcceptor\n    {\n    public:\n      RiemannSolverParameters(const std::string &subsection = \"/RiemannSolver\")\n          : ParameterAcceptor(subsection)\n      {\n      }\n    };\n\n\n    /**\n     * Specialized approximative solver for the 1D Riemann problem of the\n     * barotropic Euler equations. The solver ensures that the estimate\n     * \\f$\\lambda_{\\text{max}}\\f$ that is returned by compute() is a\n     * guaranteed upper bound of the maximal wavespeed.\n     *\n     * @ingroup EulerEquations\n     */\n    template <int dim, typename Number = double>\n    class RiemannSolver\n    {\n    public:\n      /**\n       * @name Typedefs and constexpr constants\n       */\n      //@{\n\n      using View = HyperbolicSystemView<dim, Number>;\n\n      using ScalarNumber = typename View::ScalarNumber;\n\n      static constexpr auto problem_dimension = View::problem_dimension;\n\n      using state_type = typename View::state_type;\n\n      /**\n       * Number of components in a primitive state, we store \\f$[v, a]\\f$.\n       */\n      static constexpr unsigned int riemann_data_size = 2;\n\n      /**\n       * The array type to store the primitive state for the Riemann solver\n       * \\f$[v, a]\\f$\n       */\n      using primitive_type = typename std::array<Number, riemann_data_size>;\n\n      using precomputed_type = typename View::precomputed_type;\n\n      using PrecomputedVector = typename View::PrecomputedVector;\n\n      using Parameters = RiemannSolverParameters<ScalarNumber>;\n\n      //@}\n      /**\n       * @name Compute wavespeed estimates\n       */\n      //@{\n\n      /**\n       * Constructor taking a HyperbolicSystem instance as argument\n       */\n      RiemannSolver(const HyperbolicSystem &hyperbolic_system,\n                    const Parameters &parameters,\n                    const PrecomputedVector &precomputed_values)\n          : hyperbolic_system(hyperbolic_system)\n          , parameters(parameters)\n          , precomputed_values(precomputed_values)\n      {\n      }\n\n      /**\n       * For two given 1D primitive states riemann_data_i and\n       * riemann_data_j, compute an estimate for an upper bound of the\n       * maximum wavespeed lambda.\n       */\n      Number compute(const primitive_type &riemann_data_i,\n                     const primitive_type &riemann_data_j) const;\n\n      /**\n       * For two given states U_i a U_j and a (normalized) \"direction\" n_ij\n       * compute an estimate for an upper bound of the maximum wavespeed\n       * lambda.\n       */\n      Number compute(const state_type &U_i,\n                     const state_type &U_j,\n                     const unsigned int i,\n                     const unsigned int *js,\n                     const dealii::Tensor<1, dim, Number> &n_ij) const;\n      //@}\n\n    protected:\n      /** @name Internal functions used in the Riemann solver */\n      //@{\n\n    private:\n      const HyperbolicSystem &hyperbolic_system;\n      const Parameters &parameters;\n      const PrecomputedVector &precomputed_values;\n      //@}\n    };\n  } // namespace EulerBarotropic\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/euler_barotropic/riemann_solver.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include \"riemann_solver.h\"\n\n#include <newton.h>\n#include <simd.h>\n\n// #define DEBUG_RIEMANN_SOLVER\n\nnamespace ryujin\n{\n  namespace EulerBarotropic\n  {\n    template <int dim, typename Number>\n    Number RiemannSolver<dim, Number>::compute(\n        const primitive_type &riemann_data_i,\n        const primitive_type &riemann_data_j) const\n    {\n      const auto &[u_i, a_i] = riemann_data_i;\n      const auto &[u_j, a_j] = riemann_data_j;\n\n#ifdef DEBUG_RIEMANN_SOLVER\n      std::cout << \"u_left: \" << u_i << std::endl;\n      std::cout << \"a_left: \" << a_i << std::endl;\n      std::cout << \"u_right: \" << u_j << std::endl;\n      std::cout << \"a_right: \" << a_j << std::endl;\n#endif\n\n      const Number lambda_max =\n          std::max(std::abs(u_i) + a_i, std::abs(u_j) + a_j);\n      return lambda_max;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number RiemannSolver<dim, Number>::compute(\n        const state_type &U_i,\n        const state_type &U_j,\n        const unsigned int i,\n        const unsigned int *js,\n        const dealii::Tensor<1, dim, Number> &n_ij) const\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      const auto &[e_i, p_i, a_i] =\n          precomputed_values.template read_tensor<Number, precomputed_type>(i);\n\n      const auto &[e_j, p_j, a_j] =\n          precomputed_values.template read_tensor<Number, precomputed_type>(js);\n\n      const auto rho_i = view.density(U_i);\n      const auto rho_i_inverse = Number(1.0) / rho_i;\n      const auto m_i = view.momentum(U_i);\n      const auto u_i = rho_i_inverse * n_ij * m_i;\n\n      const auto rho_j = view.density(U_j);\n      const auto rho_j_inverse = Number(1.0) / rho_j;\n      const auto m_j = view.momentum(U_j);\n      const auto u_j = rho_j_inverse * n_ij * m_j;\n\n      return compute(primitive_type{u_i, a_i}, primitive_type{u_j, a_j});\n    }\n  } // namespace EulerBarotropic\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_poisson/CMakeLists.txt",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2026 by the ryujin authors\n##\n\nadd_library(obj_euler_poisson OBJECT\n  electrostatic_configuration_library.cc\n  equation_dispatch.cc\n  initial_state_library.cc\n  parabolic_module.cc\n  )\nset_target_properties(obj_euler_poisson PROPERTIES LINKER_LANGUAGE CXX)\ndeal_ii_setup_target(obj_euler_poisson)\ntarget_link_libraries(obj_euler_poisson obj_common ${EXTERNAL_TARGETS})\n# Propagate the current source directory with PUBLIC visibility\ntarget_include_directories(obj_euler_poisson PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})\n"
  },
  {
    "path": "source/euler_poisson/Makefile",
    "content": "default: all\n.PHONY: default\n\n%:\n\t@cd .. && make $@\n.PHONY: %\n"
  },
  {
    "path": "source/euler_poisson/description.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include \"../euler/hyperbolic_system.h\"\n#include \"../euler/indicator.h\"\n#include \"../euler/limiter.h\"\n#include \"../euler/riemann_solver.h\"\n#include \"parabolic_module.h\"\n#include \"parabolic_system.h\"\n\nnamespace ryujin\n{\n  namespace EulerPoisson\n  {\n    struct Description {\n      using HyperbolicSystem = Euler::HyperbolicSystem;\n\n      template <int dim, typename Number = double>\n      using HyperbolicSystemView = Euler::HyperbolicSystemView<dim, Number>;\n\n      using ParabolicSystem = EulerPoisson::ParabolicSystem;\n\n      template <int dim, typename Number = double>\n      using ParabolicModule =\n          EulerPoisson::ParabolicModule<Description, dim, Number>;\n\n      template <int dim, typename Number = double>\n      using Indicator = Euler::Indicator<dim, Number>;\n\n      template <int dim, typename Number = double>\n      using Limiter = Euler::Limiter<dim, Number>;\n\n      template <int dim, typename Number = double>\n      using RiemannSolver = Euler::RiemannSolver<dim, Number>;\n    };\n  } // namespace EulerPoisson\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_poisson/electrostatic_configuration.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n#include <discretization.h>\n\n#include \"convenience_macros.h\"\n\n#include <deal.II/base/exceptions.h>\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/tensor.h>\n\n#include <string>\n\nnamespace ryujin\n{\n  namespace ElectrostaticConfigurationLibrary\n  {\n    /**\n     * A small abstract base class to group configuration options for an\n     * electrostatic configuration\n     *\n     * @ingroup EulerPoissonEquations\n     */\n    template <int dim, typename Number = double>\n    class ElectrostaticConfiguration : public dealii::ParameterAcceptor\n    {\n    public:\n      using curl_type = dealii::Tensor<1, dim == 2 ? 1 : dim, Number>;\n\n      ElectrostaticConfiguration(const std::string &name,\n                                 const std::string &subsection)\n          : ParameterAcceptor(subsection + \"/\" + name)\n          , name_(name)\n      {\n        dirichlet_boundaries_.insert({Boundary::do_nothing,\n                                      Boundary::slip,\n                                      Boundary::no_slip,\n                                      Boundary::dirichlet,\n                                      Boundary::dynamic,\n                                      Boundary::dirichlet_momentum,\n                                      Boundary::dirichlet_velocity});\n        this->add_parameter(\n            \"dirichlet boundaries\",\n            dirichlet_boundaries_,\n            \"A list of hyperbolic boundary types where homogeneous boundary \"\n            \"conditions will be enforced on the potential.\");\n\n        is_time_dependent_ = false;\n      }\n\n      /**\n       * Return a background (charge) density that is added to the (fluid)\n       * density when enforcing the Gauß law.\n       */\n      virtual double background_density(const dealii::Point<dim> &point,\n                                        Number t) const = 0;\n\n      /**\n       * Return a background magnetic field.\n       */\n      virtual curl_type magnetic_field(const dealii::Point<dim> &point,\n                                       Number t) const = 0;\n\n      /**\n       * Return the name of the configuration as a (const reference)\n       * std::string.\n       */\n      ACCESSOR_READ_ONLY(name)\n\n      /**\n       * Return the selected boundary type.\n       */\n      ACCESSOR_READ_ONLY(dirichlet_boundaries)\n\n      /**\n       * Return a boolean indicating whether the background fields are time\n       * dependent.\n       */\n      ACCESSOR_READ_ONLY(is_time_dependent)\n\n    protected:\n      bool is_time_dependent_;\n\n    private:\n      const std::string name_;\n      std::set<dealii::types::boundary_id> dirichlet_boundaries_;\n    };\n  } // namespace ElectrostaticConfigurationLibrary\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/euler_poisson/electrostatic_configuration_constant.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"electrostatic_configuration.h\"\n\nnamespace ryujin\n{\n  namespace ElectrostaticConfigurationLibrary\n  {\n    /**\n     * A user-specified equation of state\n     *\n     * @ingroup EulerEquations\n     */\n    template <int dim, typename Number = double>\n    class Constant : public ElectrostaticConfiguration<dim, Number>\n    {\n    public:\n      using curl_type = ElectrostaticConfiguration<dim, Number>::curl_type;\n\n      Constant(const std::string &subsection)\n          : ElectrostaticConfiguration<dim, Number>(\"constant\", subsection)\n      {\n        background_density_ = 0.;\n        this->add_parameter(\"background density\",\n                            background_density_,\n                            \"a constant background (charge) density value\");\n\n        magnetic_field_ = curl_type{};\n        this->add_parameter(\"magnetic field\",\n                            magnetic_field_,\n                            \"a constant background magnetic field density\");\n      }\n\n      double background_density(const dealii::Point<dim> & /*point*/,\n                                Number /*t*/) const final\n      {\n        return background_density_;\n      }\n\n      curl_type magnetic_field(const dealii::Point<dim> & /*point*/,\n                               Number /*t*/) const final\n      {\n        return magnetic_field_;\n      }\n\n    private:\n      double background_density_;\n      curl_type magnetic_field_;\n    };\n  } // namespace ElectrostaticConfigurationLibrary\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_poisson/electrostatic_configuration_function.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"electrostatic_configuration.h\"\n\n#include <deal.II/base/function_parser.h>\n\nnamespace ryujin\n{\n  namespace ElectrostaticConfigurationLibrary\n  {\n    /**\n     * A user-specified equation of state\n     *\n     * @ingroup EulerEquations\n     */\n    template <int dim, typename Number = double>\n    class Function : public ElectrostaticConfiguration<dim, Number>\n    {\n    public:\n      using curl_type = ElectrostaticConfiguration<dim, Number>::curl_type;\n\n      Function(const std::string &subsection)\n          : ElectrostaticConfiguration<dim, Number>(\"function\", subsection)\n      {\n        this->add_parameter(\"time dependent\",\n                            this->is_time_dependent_,\n                            \"Controls whether the function parsers support \"\n                            \"time dependent function expressions\");\n\n        background_density_expression_ = \"0.\";\n        this->add_parameter(\"background density\",\n                            background_density_expression_,\n                            \"A function expression for the background density\");\n\n        if constexpr (dim >= 2) {\n          magnetic_field_z_expression_ = \"0.\";\n          this->add_parameter(\"magnetic field z\",\n                              magnetic_field_z_expression_,\n                              \"A function expression for the z component of \"\n                              \"the magnetic field\");\n        }\n\n        if constexpr (dim == 3) {\n          magnetic_field_x_expression_ = \"0.\";\n          this->add_parameter(\"magnetic field x\",\n                              magnetic_field_x_expression_,\n                              \"A function expression for the x component of \"\n                              \"the magnetic field\");\n\n          magnetic_field_y_expression_ = \"0.\";\n          this->add_parameter(\"magnetic field y\",\n                              magnetic_field_y_expression_,\n                              \"A function expression for the y component of \"\n                              \"the magnetic field\");\n        }\n\n        /* Set up all muparser objects: */\n\n        const auto set_up_muparser = [this] {\n          /*\n           * Add variable \"t\" in case of a time-dependent function:\n           */\n          using FP = dealii::FunctionParser<dim>;\n          auto variable_names = FP::default_variable_names();\n          if (this->is_time_dependent_)\n            variable_names += \",t\";\n\n          background_density_ = std::make_unique<FP>(\n              background_density_expression_, \"\", variable_names);\n\n          if constexpr (dim >= 2) {\n            magnetic_field_z_ = std::make_unique<FP>(\n                magnetic_field_z_expression_, \"\", variable_names);\n          }\n\n          if constexpr (dim == 3) {\n            magnetic_field_x_ = std::make_unique<FP>(\n                magnetic_field_x_expression_, \"\", variable_names);\n            magnetic_field_y_ = std::make_unique<FP>(\n                magnetic_field_y_expression_, \"\", variable_names);\n          }\n        };\n\n        set_up_muparser();\n        this->parse_parameters_call_back.connect(set_up_muparser);\n      }\n\n      virtual double background_density(const dealii::Point<dim> &point,\n                                        Number t) const final\n      {\n        background_density_->set_time(t);\n        return static_cast<Number>(background_density_->value(point));\n      }\n\n      virtual curl_type magnetic_field(const dealii::Point<dim> &point,\n                                       Number t) const final\n      {\n        if constexpr (dim == 1) {\n          return curl_type{};\n        }\n\n        if constexpr (dim == 2) {\n          magnetic_field_z_->set_time(t);\n          return curl_type{\n              {static_cast<Number>(magnetic_field_z_->value(point))}};\n        }\n\n        if constexpr (dim == 3) {\n          magnetic_field_x_->set_time(t);\n          magnetic_field_y_->set_time(t);\n          magnetic_field_z_->set_time(t);\n          return curl_type{\n              {static_cast<Number>(magnetic_field_x_->value(point)),\n               static_cast<Number>(magnetic_field_y_->value(point)),\n               static_cast<Number>(magnetic_field_z_->value(point))}};\n        }\n      }\n\n    private:\n      std::string background_density_expression_;\n      std::string magnetic_field_x_expression_;\n      std::string magnetic_field_y_expression_;\n      std::string magnetic_field_z_expression_;\n\n      std::unique_ptr<dealii::FunctionParser<dim>> background_density_;\n      std::unique_ptr<dealii::FunctionParser<dim>> magnetic_field_x_;\n      std::unique_ptr<dealii::FunctionParser<dim>> magnetic_field_y_;\n      std::unique_ptr<dealii::FunctionParser<dim>> magnetic_field_z_;\n    };\n  } // namespace ElectrostaticConfigurationLibrary\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_poisson/electrostatic_configuration_library.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2026 by the ryujin authors\n//\n\n#include \"electrostatic_configuration_library.template.h\"\n\nnamespace ryujin\n{\n  namespace ElectrostaticConfigurationLibrary\n  {\n    template void populate_electrostatic_configuration_list<1, double>(\n        electrostatic_configuration_list_type<1, double> &,\n        const std::string &);\n    template void populate_electrostatic_configuration_list<2, double>(\n        electrostatic_configuration_list_type<2, double> &,\n        const std::string &);\n    template void populate_electrostatic_configuration_list<3, double>(\n        electrostatic_configuration_list_type<3, double> &,\n        const std::string &);\n\n    template void populate_electrostatic_configuration_list<1, float>(\n        electrostatic_configuration_list_type<1, float> &, const std::string &);\n    template void populate_electrostatic_configuration_list<2, float>(\n        electrostatic_configuration_list_type<2, float> &, const std::string &);\n    template void populate_electrostatic_configuration_list<3, float>(\n        electrostatic_configuration_list_type<3, float> &, const std::string &);\n  } // namespace ElectrostaticConfigurationLibrary\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_poisson/electrostatic_configuration_library.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"electrostatic_configuration.h\"\n\nnamespace ryujin\n{\n  namespace ElectrostaticConfigurationLibrary\n  {\n    template <int dim, typename Number>\n    using electrostatic_configuration_list_type =\n        std::set<std::shared_ptr<ElectrostaticConfiguration<dim, Number>>>;\n\n    /**\n     * Populate a given container with all equation of states defined in\n     * this namespace.\n     *\n     * @ingroup EulerEquations\n     */\n    template <int dim, typename Number>\n    void populate_electrostatic_configuration_list(\n        electrostatic_configuration_list_type<dim, Number> &list,\n        const std::string &subsection);\n\n  } // namespace ElectrostaticConfigurationLibrary\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_poisson/electrostatic_configuration_library.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include \"electrostatic_configuration_constant.h\"\n#include \"electrostatic_configuration_function.h\"\n#include \"electrostatic_configuration_library.h\"\n\nnamespace ryujin\n{\n  namespace ElectrostaticConfigurationLibrary\n  {\n    template <int dim, typename Number>\n    using electrostatic_configuration_list_type =\n        std::set<std::shared_ptr<ElectrostaticConfiguration<dim, Number>>>;\n\n    template <int dim, typename Number>\n    void populate_electrostatic_configuration_list(\n        electrostatic_configuration_list_type<dim, Number> &list,\n        const std::string &subsection)\n    {\n      auto add = [&](auto &&object) { list.emplace(std::move(object)); };\n      add(std::make_shared<Constant<dim, Number>>(subsection));\n      add(std::make_shared<Function<dim, Number>>(subsection));\n    }\n  } // namespace ElectrostaticConfigurationLibrary\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_poisson/equation_dispatch.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2024 - 2026 by the ryujin authors\n//\n\n#include \"description.h\"\n\n#include <compile_time_options.h>\n#include <equation_dispatch.h>\n\nnamespace ryujin\n{\n  namespace EulerPoisson\n  {\n    Dispatch<Description, NUMBER> dispatch_instance(\"euler poisson\");\n  } // namespace EulerPoisson\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_poisson/initial_state_library.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2026 by the ryujin authors\n//\n\n#include \"initial_state_library.template.h\"\n\nnamespace ryujin\n{\n  template class InitialStateLibrary<Description, 1, NUMBER>;\n  template class InitialStateLibrary<Description, 2, NUMBER>;\n  template class InitialStateLibrary<Description, 3, NUMBER>;\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_poisson/initial_state_library.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include \"description.h\"\n\n#include \"../euler/initial_state_library_euler.h\"\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  using Description = EulerPoisson::Description;\n\n  template <int dim, typename Number>\n  class InitialStateLibrary<Description, dim, Number>\n  {\n  public:\n    using HyperbolicSystem = typename Description::HyperbolicSystem;\n    using ParabolicSystem = typename Description::ParabolicSystem;\n\n    using View =\n        typename Description::template HyperbolicSystemView<dim, Number>;\n\n    using initial_state_list_type =\n        std::set<std::unique_ptr<InitialState<Description, dim, Number>>>;\n\n    static void\n    populate_initial_state_list(initial_state_list_type &initial_state_list,\n                                const HyperbolicSystem &h,\n                                const ParabolicSystem & /*p*/,\n                                const std::string &s)\n    {\n      EulerInitialStates::populate_initial_state_list<Description, dim, Number>(\n          initial_state_list, h, s);\n    }\n  };\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_poisson/instantiate.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2026 by the ryujin authors\n//\n\n#ifndef RYUJIN_INCLUDE_INSTANTIATION_ONCE\n#define RYUJIN_INCLUDE_INSTANTIATION_ONCE\n#else\n#error Instantiation files can only be included once.\n#endif\n\n#include \"description.h\"\n\nnamespace ryujin\n{\n  using EulerPoisson::Description;\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_poisson/laplace_operator.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include <deal.II/base/config.h>\n#include <loop.h>\n#include <observer_pointer.h>\n#include <offline_data.h>\n#include <simd.h>\n\n#include <deal.II/base/vectorization.h>\n#include <deal.II/dofs/dof_tools.h>\n#include <deal.II/lac/diagonal_matrix.h>\n#include <deal.II/lac/precondition.h>\n#include <deal.II/matrix_free/fe_evaluation.h>\n#include <deal.II/matrix_free/matrix_free.h>\n#include <deal.II/matrix_free/tools.h>\n#include <deal.II/multigrid/mg_base.h>\n#include <deal.II/multigrid/mg_coarse.h>\n#include <deal.II/multigrid/mg_matrix.h>\n#include <deal.II/multigrid/mg_smoother.h>\n#include <deal.II/multigrid/mg_transfer_matrix_free.h>\n#include <deal.II/multigrid/multigrid.h>\n\nnamespace ryujin\n{\n  template <int dim, typename Number, typename Number2>\n  DEAL_II_ALWAYS_INLINE inline dealii::Tensor<1, dim, Number> apply_B_n(\n      const dealii::Tensor<1, (dim == 2 ? 1 : dim), Number> &magnetic_field,\n      const Number2 theta_tau,\n      const dealii::Tensor<1, dim, Number> &velocity)\n  {\n    if constexpr (dim == 1) {\n      return velocity;\n\n    } else if constexpr (dim == 2) {\n      return velocity -\n             theta_tau * magnetic_field[0] * cross_product_2d(velocity);\n\n    } else {\n      return velocity - theta_tau * cross_product_3d(velocity, magnetic_field);\n    }\n  }\n\n\n  template <int dim, typename Number, typename Number2>\n  DEAL_II_ALWAYS_INLINE inline dealii::Tensor<1, dim, Number> apply_B_n_inverse(\n      const dealii::Tensor<1, (dim == 2 ? 1 : dim), Number> &magnetic_field,\n      const Number2 &theta_tau,\n      const dealii::Tensor<1, dim, Number> &velocity)\n  {\n    const auto denominator =\n        Number(1.) + theta_tau * theta_tau * magnetic_field.norm_square();\n\n    if constexpr (dim == 1) {\n      return velocity;\n\n    } else if constexpr (dim == 2) {\n      const auto numerator =\n          velocity + theta_tau * magnetic_field[0] * cross_product_2d(velocity);\n      return numerator / denominator;\n\n    } else {\n      const auto numerator =\n          velocity + theta_tau * cross_product_3d(velocity, magnetic_field) +\n          theta_tau * theta_tau * (velocity * magnetic_field) * magnetic_field;\n      return numerator / denominator;\n    }\n  }\n\n\n#ifndef DOXYGEN\n  template <typename T, typename... Args>\n  void create(std::unique_ptr<T> &ptr, Args &&...args)\n  {\n    ptr = std::make_unique<T>(args...);\n  }\n#endif\n\n\n  /**\n   * A matrix-free operator that implements the action of the Laplace\n   * operator.\n   *\n   * @ingroup ParabolicModule\n   */\n  template <int dim, typename Number>\n  class LaplaceOperator : public dealii::EnableObserverPointer\n  {\n  public:\n    // FIXME: refactor\n    static constexpr unsigned int order_fe = 1;\n    static constexpr unsigned int order_quad = 2;\n\n    using ScalarHostVector = Vectors::ScalarHostVector<Number>;\n\n    LaplaceOperator() = default;\n\n    void initialize(const dealii::MatrixFree<dim, Number> &matrix_free)\n    {\n      matrix_free_ = &matrix_free;\n    }\n\n    dealii::types::global_dof_index m() const\n    {\n      return matrix_free_->get_vector_partitioner(0)->size();\n    }\n\n    Number el(const unsigned int, const unsigned int) const\n    {\n      Assert(false, dealii::ExcNotImplemented());\n      return Number();\n    }\n\n    void vmult(ScalarHostVector &dst, const ScalarHostVector &src) const\n    {\n      Assert(dst.get_partitioner() == src.get_partitioner(),\n             dealii::ExcMessage(\"src and dst have 2 different partitioners\"));\n\n      using namespace dealii;\n\n      const auto body = [this](const auto &data,\n                               auto &dst,\n                               const auto &src,\n                               const auto range) {\n        FEEvaluation<dim, order_fe, order_quad, /*components*/ 1, Number> fee(\n            data, /*CG*/ 0, /*full quadrature*/ 0);\n\n        for (unsigned int cell = range.first; cell < range.second; ++cell) {\n          fee.reinit(cell);\n          fee.read_dof_values(src);\n          apply_local_operator(fee);\n          fee.distribute_local_to_global(dst);\n        }\n      };\n\n      matrix_free_->template cell_loop<ScalarHostVector, ScalarHostVector>(\n          body, dst, src, /*zero destination*/ true);\n    }\n\n    void Tvmult(ScalarHostVector &dst, const ScalarHostVector &src) const\n    {\n      vmult(dst, src);\n    }\n\n    void compute_diagonal(\n        dealii::DiagonalMatrix<ScalarHostVector> &diagonal_matrix) const\n    {\n      ScalarHostVector &diagonal_vector = diagonal_matrix.get_vector();\n      matrix_free_->initialize_dof_vector(diagonal_vector, /*CG*/ 0);\n\n      dealii::MatrixFreeTools::compute_diagonal(\n          *matrix_free_,\n          diagonal_vector,\n          &LaplaceOperator::template apply_local_operator<\n              dealii::FEEvaluation<dim, -1, 0, 1, Number>>,\n          this,\n          0,\n          1);\n\n      /* invert diagonal matrix: */\n\n      const auto n_owned_cg =\n          diagonal_vector.get_partitioner()->locally_owned_size();\n\n      const auto body_invert = [&](auto sentinel, const unsigned int i) {\n        constexpr Number eps = std::numeric_limits<Number>::epsilon();\n        using T = decltype(sentinel);\n        const auto m_i = read_entry<T>(diagonal_vector, i);\n        constexpr auto gt = dealii::SIMDComparison::greater_than;\n        const auto m_i_inv = dealii::compare_and_apply_mask<gt>(\n            std::abs(m_i), T(eps), Number(1.) / m_i, T(1.));\n        write_entry<T>(diagonal_vector, m_i_inv, i);\n      };\n      cpu_simd_loop<Number>(\"\", body_invert, 0, n_owned_cg, n_owned_cg);\n    }\n\n  private:\n    const dealii::MatrixFree<dim, Number> *matrix_free_;\n\n    template <typename Evaluator>\n    void apply_local_operator(Evaluator &eval) const\n    {\n      eval.evaluate(dealii::EvaluationFlags::gradients);\n      for (const unsigned int q : eval.quadrature_point_indices())\n        eval.submit_gradient(eval.get_gradient(q), q);\n      eval.integrate(dealii::EvaluationFlags::gradients);\n    }\n  };\n\n\n  /**\n   * A matrix-free operator that implements the action of the Laplace\n   * operator.\n   *\n   * @ingroup EulerPoissonEquations\n   */\n  template <int dim, typename Number>\n  class UpdateOperator : public dealii::EnableObserverPointer\n  {\n  public:\n    // FIXME: refactor\n    static constexpr unsigned int order_fe = 1;\n    static constexpr unsigned int order_quad = 2;\n\n    using ScalarHostVector = Vectors::ScalarHostVector<Number>;\n    using BlockHostVector = Vectors::BlockHostVector<Number>;\n\n    UpdateOperator() = default;\n\n    void initialize(const dealii::MatrixFree<dim, Number> &matrix_free,\n                    const ScalarHostVector &density,\n                    const BlockHostVector &magnetic_field)\n    {\n      matrix_free_ = &matrix_free;\n      density_ = &density;\n      magnetic_field_ = &magnetic_field;\n\n      theta_tau_ = Number(0.);\n      alpha_ = Number(0.);\n    }\n\n    dealii::types::global_dof_index m() const\n    {\n      return matrix_free_->get_vector_partitioner(0)->size();\n    }\n\n    Number el(const unsigned int, const unsigned int) const\n    {\n      Assert(false, dealii::ExcNotImplemented());\n      return Number();\n    }\n\n    void set_theta_tau(const Number theta_tau) const\n    {\n      theta_tau_ = theta_tau;\n    }\n\n    void set_alpha(const Number alpha) const\n    {\n      alpha_ = alpha;\n    }\n\n    void vmult(ScalarHostVector &dst, const ScalarHostVector &src) const\n    {\n      Assert(dst.get_partitioner() == src.get_partitioner(),\n             dealii::ExcMessage(\"src and dst have 2 different partitioners\"));\n\n      using namespace dealii;\n\n      const auto body_laplace = [](const auto &data,\n                                   auto &dst,\n                                   const auto &src,\n                                   const auto range) {\n        FEEvaluation<dim, order_fe, order_quad, /*components*/ 1, Number> fee(\n            data, /*CG*/ 0, /*full quadrature*/ 0);\n\n        for (unsigned int cell = range.first; cell < range.second; ++cell) {\n          fee.reinit(cell);\n          fee.gather_evaluate(src, dealii::EvaluationFlags::gradients);\n          for (unsigned int q = 0; q < fee.n_q_points; ++q)\n            fee.submit_gradient(fee.get_gradient(q), q);\n          fee.integrate_scatter(dealii::EvaluationFlags::gradients, dst);\n        }\n      };\n\n      matrix_free_->template cell_loop<ScalarHostVector, ScalarHostVector>(\n          body_laplace, dst, src, /*zero destination*/ true);\n\n      const auto body_velocity = [this](const auto &data,\n                                        auto &dst,\n                                        const auto &src,\n                                        const auto range) {\n        FEEvaluation<dim, order_fe, order_quad, /*components*/ 1, Number> fee(\n            data, /*CG*/ 0, /*lumped quadrature*/ 1);\n        FEEvaluation<dim, order_fe, order_quad, /*components*/ 1, Number>\n            fee_density(data, /*hyperbolic*/ 1, /*lumped quadrature*/ 1);\n        FEEvaluation<dim,\n                     order_fe,\n                     order_quad,\n                     /*components*/ (dim == 2 ? 1 : dim),\n                     Number>\n            fee_magnetic(data, /*hyperbolic*/ 1, /*lumped quadrature*/ 1);\n\n        for (unsigned int cell = range.first; cell < range.second; ++cell) {\n          fee.reinit(cell);\n          fee_density.reinit(cell);\n          fee_magnetic.reinit(cell);\n\n          fee.gather_evaluate(src, dealii::EvaluationFlags::gradients);\n          fee_density.gather_evaluate(*density_,\n                                      dealii::EvaluationFlags::values);\n          fee_magnetic.gather_evaluate(*magnetic_field_,\n                                       dealii::EvaluationFlags::values);\n\n          for (unsigned int q = 0; q < fee.n_q_points; ++q) {\n            const auto grad_phi = fee.get_gradient(q);\n            auto density = fee_density.get_value(q);\n            dealii::Tensor<1, (dim == 2 ? 1 : dim), decltype(density)>\n                magnetic_field;\n            if constexpr (dim == 2) {\n              magnetic_field[0] = fee_magnetic.get_value(q);\n            } else if constexpr (dim == 3) {\n              magnetic_field = fee_magnetic.get_value(q);\n            }\n\n            const auto B_n_inverse_grad_phi =\n                apply_B_n_inverse(magnetic_field, theta_tau_, grad_phi);\n            const auto result = theta_tau_ * theta_tau_ * alpha_ * density *\n                                B_n_inverse_grad_phi;\n            fee.submit_gradient(result, q);\n          }\n          fee.integrate_scatter(dealii::EvaluationFlags::gradients, dst);\n        }\n      };\n\n      matrix_free_->template cell_loop<ScalarHostVector, ScalarHostVector>(\n          body_velocity, dst, src, /*zero destination*/ false);\n    }\n\n    void Tvmult(ScalarHostVector &dst, const ScalarHostVector &src) const\n    {\n      vmult(dst, src);\n    }\n\n  private:\n    const dealii::MatrixFree<dim, Number> *matrix_free_;\n    const ScalarHostVector *density_;\n    const BlockHostVector *magnetic_field_;\n\n    mutable Number theta_tau_;\n    mutable Number alpha_;\n  };\n\n\n  template <int dim, typename Number>\n  class MGTransfer : public dealii::MGTransferMatrixFree<dim, Number>\n  {\n  public:\n    void build(const dealii::DoFHandler<dim> &dof_handler,\n               const dealii::MGConstrainedDoFs &mg_constrained_dofs,\n               const dealii::MGLevelObject<dealii::MatrixFree<dim, Number>>\n                   &matrix_free)\n    {\n      dealii::MGTransferMatrixFree<dim, Number>::initialize_constraints(\n          mg_constrained_dofs);\n      dealii::MGTransferMatrixFree<dim, Number>::build(dof_handler);\n      level_matrix_free_ = &matrix_free;\n    }\n\n    template <typename Number2>\n    void copy_to_mg(\n        const dealii::DoFHandler<dim> &dof_handler,\n        dealii::MGLevelObject<\n            dealii::LinearAlgebra::distributed::Vector<Number>> &dst,\n        const dealii::LinearAlgebra::distributed::Vector<Number2> &src) const\n    {\n      if (dst[dst.min_level()].size() == 0)\n        for (unsigned int l = dst.min_level(); l <= dst.max_level(); ++l)\n          (*level_matrix_free_)[l].initialize_dof_vector(dst[l]);\n      dealii::MGTransferMatrixFree<dim, Number>::copy_to_mg(\n          dof_handler, dst, src);\n    }\n\n  private:\n    const dealii::MGLevelObject<dealii::MatrixFree<dim, Number>>\n        *level_matrix_free_;\n  };\n\n\n  template <int dim, typename Number>\n  class MGSmoother : public dealii::EnableObserverPointer\n  {\n  public:\n    // FIXME: refactor\n    static constexpr unsigned int order_fe = 1;\n    static constexpr unsigned int order_quad = 2;\n\n    using ScalarHostVector = Vectors::ScalarHostVector<Number>;\n    using ScalarHostVectorFloat = Vectors::ScalarHostVector<float>;\n\n    using Preconditioner =\n        dealii::PreconditionChebyshev<LaplaceOperator<dim, float>,\n                                      ScalarHostVectorFloat>;\n\n    MGSmoother() = default;\n\n    struct MultigridParameters {\n      unsigned int gmg_max_iter;\n      double gmg_smoother_range;\n      double gmg_smoother_max_eig;\n      unsigned int gmg_smoother_degree;\n      unsigned int gmg_smoother_n_cg_iter;\n      unsigned int gmg_min_level;\n      double gmg_coarse_tolerance;\n    };\n\n    void initialize(const OfflineData<dim, Number> &offline_data,\n                    const std::set<dealii::types::boundary_id> boundary_ids,\n                    const MultigridParameters parameters)\n    {\n      using namespace dealii;\n\n      /*\n       * Set up multigrid operators and data structures:\n       */\n\n      const auto &discretization = offline_data.discretization();\n      const auto &triangulation = discretization.triangulation();\n      const unsigned int n_levels = triangulation.n_global_levels();\n      const unsigned int min_level =\n          std::min(parameters.gmg_min_level, n_levels - 1);\n      MGLevelObject<IndexSet> relevant_sets(0, n_levels - 1);\n\n      const auto &dof_handler = offline_data.dof_handler_cg();\n      for (unsigned int level = 0; level < n_levels; ++level) {\n#if DEAL_II_VERSION_GTE(9, 6, 0)\n        relevant_sets[level] =\n            dealii::DoFTools::extract_locally_relevant_level_dofs( //\n                dof_handler,\n                level);\n#else\n        dealii::DoFTools::extract_locally_relevant_level_dofs(\n            dof_handler, level, relevant_sets[level]);\n#endif\n      }\n\n      // First index CG, second index hyperbolic ansatz\n      std::vector<const dealii::DoFHandler<dim> *> dof_handlers = {\n          &dof_handler, &offline_data.dof_handler()};\n\n      mg_constrained_dofs_.initialize(dof_handler, relevant_sets);\n      /* FIXME: handle periodic boundary conditions and hanging nodes... */\n      if (!boundary_ids.empty())\n        mg_constrained_dofs_.make_zero_boundary_constraints( //\n            dof_handler,\n            boundary_ids);\n\n      // First index full quadrature, second index lumped quadrature\n      std::vector<dealii::Quadrature<1>> quadratures = {\n          discretization.quadrature_1d()[0],\n          discretization.nodal_quadrature_1d()[0]};\n\n      typename MatrixFree<dim, float>::AdditionalData additional_data_level;\n      additional_data_level.tasks_parallel_scheme =\n          MatrixFree<dim, float>::AdditionalData::none;\n      level_matrix_free_.resize(min_level, n_levels - 1);\n\n      for (unsigned int level = min_level; level < n_levels; ++level) {\n        additional_data_level.mg_level = level;\n\n#if DEAL_II_VERSION_GTE(9, 6, 0)\n        AffineConstraints<float> level_constraints(relevant_sets[level],\n                                                   relevant_sets[level]);\n#else\n        AffineConstraints<float> level_constraints(relevant_sets[level]);\n#endif\n\n        if (!boundary_ids.empty()) {\n          level_constraints.add_lines(\n              mg_constrained_dofs_.get_boundary_indices(level));\n#if DEAL_II_VERSION_GTE(9, 6, 0)\n          level_constraints.merge(\n              mg_constrained_dofs_.get_level_constraints(level));\n#endif\n        }\n        level_constraints.close();\n\n        AffineConstraints<float> dummy;\n        dummy.close();\n        std::vector<const dealii::AffineConstraints<float> *>\n            level_constraints_list = {&level_constraints, &dummy};\n\n        level_matrix_free_[level].reinit(discretization.mapping(),\n                                         dof_handlers,\n                                         level_constraints_list,\n                                         quadratures,\n                                         additional_data_level);\n      }\n\n      mg_transfer_.build(dof_handler, mg_constrained_dofs_, level_matrix_free_);\n\n      level_laplace_matrices_.resize(level_matrix_free_.min_level(),\n                                     level_matrix_free_.max_level());\n\n      MGLevelObject<typename Preconditioner::AdditionalData> smoother_data(\n          level_matrix_free_.min_level(), level_matrix_free_.max_level());\n\n      for (unsigned int level = level_matrix_free_.min_level();\n           level <= level_matrix_free_.max_level();\n           ++level) {\n\n        level_laplace_matrices_[level].initialize(level_matrix_free_[level]);\n        smoother_data[level].preconditioner =\n            std::make_shared<dealii::DiagonalMatrix<ScalarHostVectorFloat>>();\n        level_laplace_matrices_[level].compute_diagonal(\n            *smoother_data[level].preconditioner);\n\n#if DEAL_II_VERSION_GTE(9, 6, 0)\n        if (boundary_ids.empty()) {\n          smoother_data[level].eigenvalue_algorithm =\n              dealii::internal::EigenvalueAlgorithm::power_iteration;\n        }\n#endif\n\n        if (level == level_matrix_free_.min_level()) {\n          smoother_data[level].degree = numbers::invalid_unsigned_int;\n          smoother_data[level].eig_cg_n_iterations = 500;\n          smoother_data[level].smoothing_range = 1e-3;\n        } else {\n          smoother_data[level].degree = parameters.gmg_smoother_degree;\n          smoother_data[level].eig_cg_n_iterations =\n              parameters.gmg_smoother_n_cg_iter;\n          smoother_data[level].smoothing_range = parameters.gmg_smoother_range;\n          if (parameters.gmg_smoother_n_cg_iter == 0)\n            smoother_data[level].max_eigenvalue =\n                parameters.gmg_smoother_max_eig;\n        }\n      }\n\n      relaxation_.initialize(level_laplace_matrices_, smoother_data);\n\n      /*\n       * Set up coarse solver:\n       */\n\n      create(coarse_solver_control_, 10000, parameters.gmg_coarse_tolerance);\n      create(coarse_solver_data_);\n      create(coarse_solver_, *coarse_solver_control_, *coarse_solver_data_);\n      create(coarse_preconditioner_);\n      level_laplace_matrices_[level_laplace_matrices_.min_level()]\n          .compute_diagonal(*coarse_preconditioner_);\n      create(coarse_grid_solver_,\n             *coarse_solver_,\n             level_laplace_matrices_[level_laplace_matrices_.min_level()],\n             *coarse_preconditioner_);\n\n      /*\n       * Set up preconditioner:\n       */\n\n      create(mg_matrix_, level_laplace_matrices_);\n      create(mg_,\n             *mg_matrix_,\n             *coarse_grid_solver_,\n             mg_transfer_,\n             relaxation_,\n             relaxation_,\n             level_laplace_matrices_.min_level(),\n             level_laplace_matrices_.max_level());\n      create(preconditioner_, dof_handler, *mg_, mg_transfer_);\n    }\n\n    void vmult(ScalarHostVector &dst, const ScalarHostVector &src) const\n    {\n      Assert(dst.get_partitioner() == src.get_partitioner(),\n             dealii::ExcMessage(\"src and dst have 2 different partitioners\"));\n      preconditioner_->vmult(dst, src);\n    }\n\n    void Tvmult(ScalarHostVector &dst, const ScalarHostVector &src) const\n    {\n      vmult(dst, src);\n    }\n\n  private:\n    //@}\n    /**\n     * @name Internal data\n     */\n    //@{\n\n    dealii::MGConstrainedDoFs mg_constrained_dofs_;\n    dealii::MGLevelObject<dealii::MatrixFree<dim, float>> level_matrix_free_;\n    MGTransfer<dim, float> mg_transfer_;\n    dealii::MGLevelObject<LaplaceOperator<dim, float>> level_laplace_matrices_;\n\n    dealii::mg::SmootherRelaxation<Preconditioner, ScalarHostVectorFloat>\n        relaxation_;\n\n    std::unique_ptr<dealii::SolverControl> coarse_solver_control_;\n    using CAD =\n        typename dealii::SolverCG<ScalarHostVectorFloat>::AdditionalData;\n    std::unique_ptr<CAD> coarse_solver_data_;\n    std::unique_ptr<dealii::SolverCG<ScalarHostVectorFloat>> coarse_solver_;\n    std::unique_ptr<dealii::DiagonalMatrix<ScalarHostVectorFloat>>\n        coarse_preconditioner_;\n    using MGCGIS = dealii::MGCoarseGridIterativeSolver<\n        ScalarHostVectorFloat,\n        dealii::SolverCG<ScalarHostVectorFloat>,\n        LaplaceOperator<dim, float>,\n        dealii::DiagonalMatrix<ScalarHostVectorFloat>>;\n    std::unique_ptr<MGCGIS> coarse_grid_solver_;\n\n    std::unique_ptr<dealii::mg::Matrix<ScalarHostVectorFloat>> mg_matrix_;\n    std::unique_ptr<dealii::Multigrid<ScalarHostVectorFloat>> mg_;\n    std::unique_ptr<dealii::PreconditionMG<dim,\n                                           ScalarHostVectorFloat,\n                                           MGTransfer<dim, float>>>\n        preconditioner_;\n    //@}\n  };\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/euler_poisson/parabolic_module.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2025 - 2026 by the ryujin authors\n//\n\n#include \"description.h\"\n#include \"parabolic_module.template.h\"\n\n#define INSTANTIATE(dim, stages)                                               \\\n  template void                                                                \\\n  ParabolicModule<Description, dim, NUMBER>::backward_euler_step<stages>(      \\\n      const StateVector &,                                                     \\\n      const NUMBER,                                                            \\\n      std::array<std::reference_wrapper<const StateVector>, stages>,           \\\n      const std::array<NUMBER, stages>,                                        \\\n      StateVector &,                                                           \\\n      NUMBER) const\n\nnamespace ryujin\n{\n  namespace EulerPoisson\n  {\n    template class ParabolicModule<Description, 1, NUMBER>;\n    template class ParabolicModule<Description, 2, NUMBER>;\n    template class ParabolicModule<Description, 3, NUMBER>;\n\n    INSTANTIATE(1, 0);\n    INSTANTIATE(1, 1);\n    INSTANTIATE(1, 2);\n    INSTANTIATE(1, 3);\n\n    INSTANTIATE(2, 0);\n    INSTANTIATE(2, 1);\n    INSTANTIATE(2, 2);\n    INSTANTIATE(2, 3);\n\n    INSTANTIATE(3, 0);\n    INSTANTIATE(3, 1);\n    INSTANTIATE(3, 2);\n    INSTANTIATE(3, 3);\n  } // namespace EulerPoisson\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_poisson/parabolic_module.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"electrostatic_configuration_library.h\"\n#include \"laplace_operator.h\"\n\n#include <hyperbolic_module.h>\n#include <initial_values.h>\n#include <mpi_ensemble.h>\n#include <offline_data.h>\n\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/timer.h>\n#include <deal.II/lac/precondition.h>\n#include <deal.II/matrix_free/matrix_free.h>\n\nnamespace ryujin\n{\n  namespace EulerPoisson\n  {\n    /**\n     * Controls the chosen Gauss law restart strategy.\n     *\n     * @ingroup EulerPoissonEquations\n     */\n    enum GaussLawRestartStrategy : dealii::types::boundary_id {\n      /**\n       * Initialize the potential by solving\n       * $$\n       *   -\\Delta \\varphi = \\alpha (\\rho + \\rho_b)\n       * $$\n       * once at the first timestep. Then, solve\n       * $$\n       *   -\\partial_t \\Delta \\varphi = - \\alpha \\nabla \\cdot \\boldsymbol m +\n       *   \\partial_t \\rho_b\n       * $$\n       * at each parabolic substep. Do not restart the Gauss law. This is\n       * the default strategy.\n       */\n      no_restart,\n\n      /**\n       * Initialize the potential by solving\n       * $$\n       *   -\\Delta \\varphi = \\alpha (\\rho + \\rho_b)\n       * $$\n       * once at the first timestep. Then, solve\n       * $$\n       *   -\\partial_t \\Delta \\varphi = - \\alpha \\nabla \\cdot \\boldsymbol m +\n       *   \\partial_t \\rho_b\n       * $$\n       * at each parabolic substep. Finally, at the beginning of each\n       * subsequent time step, restart the Gauss law at the beginning of\n       * each time step by solving again\n       * $$\n       *   -\\Delta \\varphi = \\alpha (\\rho + \\rho_b).\n       * $$\n       *\n       */\n      full_restart,\n\n      /**\n       * Solve\n       * $$\n       *   -\\partial_t \\Delta \\varphi = -\\alpha \\nabla \\cdot \\boldsymbol m\n       *   + \\partial_t \\rho_b\n       * $$\n       * at each parabolic substep. Then, correct the Gauss law violation\n       * by artificially relaxing the kinetic energy.\n       *\n       * FIXME documentation\n       */\n      correction,\n\n      /**\n       * Initialize the potential by solving\n       * $$\n       * -\\Delta \\varphi = \\alpha (\\rho + \\rho_b)\n       * $$\n       * once at the first timestep. Afterwards, do not update the potential\n       * any more.\n       */\n      static_no_restart,\n\n      /**\n       * Solve\n       * $$\n       * -\\Delta \\varphi = \\alpha (\\rho + \\rho_b)\n       * $$\n       * at the beginning of each timestep. Do not update the potential\n       * during a parabolic substep.\n       */\n      static_full_restart,\n\n    };\n\n  } // namespace EulerPoisson\n} // namespace ryujin\n\n#ifndef DOXYGEN\nDECLARE_ENUM(\n    ryujin::EulerPoisson::GaussLawRestartStrategy,\n    LIST({ryujin::EulerPoisson::GaussLawRestartStrategy::no_restart,\n          \"no restart\"},\n         {ryujin::EulerPoisson::GaussLawRestartStrategy::full_restart,\n          \"full restart\"},\n         {ryujin::EulerPoisson::GaussLawRestartStrategy::correction,\n          \"correction\"},\n         {ryujin::EulerPoisson::GaussLawRestartStrategy::static_no_restart,\n          \"static no restart\"},\n         {ryujin::EulerPoisson::GaussLawRestartStrategy::static_full_restart,\n          \"static full restart\"}));\n#endif\n\nnamespace ryujin\n{\n  namespace EulerPoisson\n  {\n    /**\n     * Implicit backward-Euler time stepping for the parabolic limiting\n     * equation for the Euler-Poisson system\n     *\n     * @ingroup ParabolicModule\n     */\n    template <typename Description, int dim, typename Number>\n    class ParabolicModule final : public dealii::ParameterAcceptor\n    {\n    public:\n      /**\n       * @name Typedefs and constexpr constants\n       */\n      //@{\n\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n\n      using ParabolicSystem = typename Description::ParabolicSystem;\n\n      using StateVector = typename View::StateVector;\n\n      using ScalarHostVector = Vectors::ScalarHostVector<Number>;\n\n      using BlockHostVector = Vectors::BlockHostVector<Number>;\n\n      using ScalarNumber = typename View::ScalarNumber;\n\n      static constexpr auto problem_dimension = View::problem_dimension;\n\n      using state_type = typename View::state_type;\n\n      //@}\n\n      //@}\n      /**\n       * @name Constructor and setup\n       */\n      //@{\n\n      /**\n       * Constructor.\n       */\n      ParabolicModule(\n          const MPIEnsemble &mpi_ensemle,\n          std::map<std::string, dealii::Timer> &computing_timer,\n          const OfflineData<dim, Number> &offline_data,\n          const HyperbolicSystem &hyperbolic_system,\n          const ParabolicSystem &parabolic_system,\n          const InitialValues<Description, dim, Number> &initial_values,\n          const std::string &subsection = \"/ParabolicModule\");\n\n      /**\n       * Prepare time stepping. A call to @p prepare() allocates temporary\n       * storage and is necessary before any of the following time-stepping\n       * functions can be called.\n       */\n      void prepare();\n\n      //@}\n      /**\n       * @name Functons for performing explicit time steps\n       */\n      //@{\n\n      /**\n       * (Re)initialize the parabolic state vector component of the state\n       * vector.\n       *\n       * @note This routine does not modify the hyperbolic state vector or\n       * the precomputed vector component.\n       */\n      void reinit_state_vector(StateVector &state_vector) const;\n\n      /**\n       * This function preprocesses a given state vector @p U in preparation\n       * for a high order IMEX time step. This function exists because some\n       * time stepping variants have to precompute quantities before we can\n       * perform an IMEX step. In addition, this function is called whenever\n       * we perform a mesh transfer or output operation.\n       */\n      void prepare_state_vector(StateVector &state_vector, Number t) const;\n\n      /**\n       * Given a reference to a previous state vector @p old_U at time\n       * @p old_t and a time-step size @p tau perform an implicit backward\n       * euler step (and store the result in @p new_U).\n       *\n       * The function takes an optional array of states @p stage_U together\n       * with a an array of weights @p stage_weights to construct a modified\n       * high-order right-hand side / flux.\n       */\n      template <int stages>\n      void\n      backward_euler_step(const StateVector &old_state_vector,\n                          const Number old_t,\n                          std::array<std::reference_wrapper<const StateVector>,\n                                     stages> stage_state_vectors,\n                          const std::array<Number, stages> stage_weights,\n                          StateVector &new_state_vector,\n                          Number tau) const;\n\n      /**\n       * Given a reference to a previous state vector @p old_U at time @p\n       * old_t and a time-step size @p tau perform an implicit Crank-Nicolson\n       * step (and store the result in @p new_U).\n       *\n       * This variant is used in the TimeIntegrator class for the Strang\n       * split variants.\n       */\n      void crank_nicolson_step(const StateVector &old_state_vector,\n                               const Number old_t,\n                               StateVector &new_state_vector,\n                               Number tau) const;\n\n      /**\n       * Sets the invariant domain violation strategy.\n       */\n      void set_id_violation_strategy(const IDViolationStrategy &strategy) const\n      {\n        id_violation_strategy_ = strategy;\n      }\n\n      //@}\n      /**\n       * @name Information and statistics\n       */\n      //@{\n\n      /**\n       * Print a status line with solver statistics. This function is used\n       * for constructing the status message displayed periodically in the\n       * TimeLoop.\n       */\n      void print_solver_statistics(std::ostream &output) const;\n\n      /**\n       * The number of restarts signalled by the step() function.\n       */\n      ACCESSOR_READ_ONLY(n_restarts)\n\n      /**\n       * The number of corrections performed by the step() function. This\n       * function exists to mirror the ParabolicModule interface and will\n       * always return 0.\n       */\n      ACCESSOR_READ_ONLY(n_corrections)\n\n      /**\n       * The number of ID violation warnings encounterd in the step()\n       * function.\n       */\n      ACCESSOR_READ_ONLY(n_warnings)\n\n    private:\n      //@}\n      /**\n       * @name Run time options\n       */\n      //@{\n\n      GaussLawRestartStrategy gauss_law_restart_strategy_;\n\n      unsigned int gmg_max_iter_;\n      double gmg_smoother_range_;\n      double gmg_smoother_max_eig_;\n      unsigned int gmg_smoother_degree_;\n      unsigned int gmg_smoother_n_cg_iter_;\n      unsigned int gmg_min_level_;\n\n      Number tolerance_;\n      bool tolerance_linfty_norm_;\n\n      //@}\n      /**\n       * @name Low-level implementation\n       */\n      //@{\n\n      /**\n       * Set up affine constraints. Internally used in prepare().\n       */\n      void create_constraints();\n\n      /**\n       * Update the background density vector to time t.\n       *\n       * @note: The vector is always updated for t = 0, but the update is\n       * skipped for t > 0 if the background fields are time independent.\n       */\n      void update_background_density(const Number t) const;\n\n      /**\n       * Update the (background) magnetic field vector to time t.\n       *\n       * @note: The vector is always updated for t = 0, but the update is\n       * skipped for t > 0 if the background fields are time independent.\n       */\n      void update_magnetic_field(const Number t) const;\n\n      /**\n       * Compute the potential phi (the last component of the state_vector)\n       * for a given density (the first component of the state_vector).\n       */\n      void compute_potential(const Number t, StateVector &state_vector) const;\n\n      /**\n       * Set the velocity field equal to the magnetic magnetic drift\n       * velocity computed from the potential.\n       */\n      void enforce_magnetic_drift_velocity(StateVector &state_vector) const;\n\n      /**\n       * Given a reference to a previous state vector @p old_state_vector\n       * at time @p old_t and a time-step size @p tau perform a backward\n       * Euler time step (and store the result in @p new_state_vector).\n       *\n       * If the boolean @crank_nicolson_extrapolation is set to true, then\n       * we perform a final extrapolation on the primitive state for time\n       * t + 2 * tau.\n       */\n      void step(const StateVector &old_state_vector,\n                const Number old_t,\n                StateVector &new_state_vector,\n                Number tau,\n                const bool crank_nicolson_extrapolation) const;\n\n      //@}\n      /**\n       * @name Internal data\n       */\n      //@{\n\n      // FIXME: refactor\n      static constexpr unsigned int order_fe = 1;\n      static constexpr unsigned int order_quad = 2;\n\n      const MPIEnsemble &mpi_ensemble_;\n      std::map<std::string, dealii::Timer> &computing_timer_;\n\n      dealii::ObserverPointer<const HyperbolicSystem> hyperbolic_system_;\n      dealii::ObserverPointer<const ParabolicSystem> parabolic_system_;\n      dealii::ObserverPointer<const ryujin::OfflineData<dim, Number>>\n          offline_data_;\n      dealii::ObserverPointer<\n          const ryujin::InitialValues<Description, dim, Number>>\n          initial_values_;\n\n      ElectrostaticConfigurationLibrary::\n          electrostatic_configuration_list_type<dim, Number>\n              electrostatic_configuration_list_;\n\n      std::shared_ptr<ElectrostaticConfigurationLibrary::\n                          ElectrostaticConfiguration<dim, Number>>\n          selected_electrostatic_configuration_;\n\n      mutable IDViolationStrategy id_violation_strategy_;\n\n      mutable unsigned int cycle_;\n      mutable double n_iterations_gauss_;\n      mutable double n_iterations_step_;\n\n      mutable unsigned int n_restarts_;\n      mutable unsigned int n_corrections_;\n      mutable unsigned int n_warnings_;\n\n      dealii::MatrixFree<dim, Number> matrix_free_;\n      dealii::AffineConstraints<Number> affine_constraints_potential_;\n\n      LaplaceOperator<dim, Number> laplace_operator_;\n      dealii::DiagonalMatrix<ScalarHostVector> diagonal_preconditioner_;\n      MGSmoother<dim, Number> multigrid_preconditioner_;\n      UpdateOperator<dim, Number> update_operator_;\n\n      mutable ScalarHostVector potential_rhs_;\n      mutable ScalarHostVector density_;\n      mutable ScalarHostVector background_density_;\n      mutable BlockHostVector magnetic_field_;\n      mutable BlockHostVector velocity_rhs_;\n\n      mutable bool potential_initialized_;\n      mutable Number t_background_density_;\n      mutable Number t_magnetic_field_;\n      //@}\n    };\n  } // namespace EulerPoisson\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/euler_poisson/parabolic_module.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include \"laplace_operator.h\"\n#include \"parabolic_module.h\"\n\n#include <convenience_macros.h>\n#include <loop.h>\n#include <scope.h>\n#include <simd.h>\n\n#include <deal.II/dofs/dof_tools.h>\n#include <deal.II/lac/linear_operator.h>\n#include <deal.II/lac/precondition.h>\n#include <deal.II/lac/solver_cg.h>\n#include <deal.II/matrix_free/fe_evaluation.h>\n#include <deal.II/numerics/vector_tools.h>\n#include <deal.II/numerics/vector_tools.templates.h>\n\n\nnamespace ryujin\n{\n  namespace EulerPoisson\n  {\n    using namespace dealii;\n\n    template <typename Description, int dim, typename Number>\n    ParabolicModule<Description, dim, Number>::ParabolicModule(\n        const MPIEnsemble &mpi_ensemble,\n        std::map<std::string, dealii::Timer> &computing_timer,\n        const OfflineData<dim, Number> &offline_data,\n        const HyperbolicSystem &hyperbolic_system,\n        const ParabolicSystem &parabolic_system,\n        const InitialValues<Description, dim, Number> &initial_values,\n        const std::string &subsection)\n        : ParameterAcceptor(subsection)\n        , mpi_ensemble_(mpi_ensemble)\n        , computing_timer_(computing_timer)\n        , hyperbolic_system_(&hyperbolic_system)\n        , parabolic_system_(&parabolic_system)\n        , offline_data_(&offline_data)\n        , initial_values_(&initial_values)\n        , id_violation_strategy_(IDViolationStrategy::warn)\n        , cycle_(0)\n        , n_iterations_gauss_(0)\n        , n_iterations_step_(0)\n        , n_restarts_(0)\n        , n_corrections_(0)\n        , n_warnings_(0)\n        , potential_initialized_(false)\n        , t_background_density_(std::numeric_limits<Number>::lowest())\n        , t_magnetic_field_(std::numeric_limits<Number>::lowest())\n    {\n      gauss_law_restart_strategy_ = GaussLawRestartStrategy::no_restart;\n      add_parameter(\"gauss law restart strategy\",\n                    gauss_law_restart_strategy_,\n                    \"Strategy used when restarting the gauss law. Options are \"\n                    \"\\'no restart\\', \\'full restart\\', \\'correction\\', \"\n                    \"\\'static no restart\\', and \\'static full restart\\'.\");\n\n      gmg_max_iter_ = 15;\n      add_parameter(\"multigrid - max iter\",\n                    gmg_max_iter_,\n                    \"Maximal number of CG iterations with GMG smoother\");\n\n      gmg_smoother_range_ = 8.;\n      add_parameter(\"multigrid - chebyshev range\",\n                    gmg_smoother_range_,\n                    \"Chebyshev smoother: eigenvalue range parameter\");\n\n      gmg_smoother_max_eig_ = 2.0;\n      add_parameter(\"multigrid - chebyshev max eig\",\n                    gmg_smoother_max_eig_,\n                    \"Chebyshev smoother: maximal eigenvalue\");\n\n      gmg_smoother_degree_ = 3;\n      add_parameter(\"multigrid - chebyshev degree\",\n                    gmg_smoother_degree_,\n                    \"Chebyshev smoother: degree\");\n\n      gmg_smoother_n_cg_iter_ = 10;\n      add_parameter(\n          \"multigrid - chebyshev cg iter\",\n          gmg_smoother_n_cg_iter_,\n          \"Chebyshev smoother: number of CG iterations to approximate \"\n          \"eigenvalue\");\n\n      gmg_min_level_ = 0;\n      add_parameter(\n          \"multigrid - min level\",\n          gmg_min_level_,\n          \"Minimal mesh level to be visited in the geometric multigrid \"\n          \"cycle where the coarse grid solver (Chebyshev) is called\");\n\n      tolerance_ = Number(1.0e-12);\n      add_parameter(\"tolerance\", tolerance_, \"Tolerance for linear solvers\");\n\n      tolerance_linfty_norm_ = false;\n      add_parameter(\"tolerance linfty norm\",\n                    tolerance_linfty_norm_,\n                    \"Use the l_infty norm instead of the l_2 norm for the \"\n                    \"stopping criterion\");\n\n      ElectrostaticConfigurationLibrary::\n          populate_electrostatic_configuration_list<dim, Number>(\n              electrostatic_configuration_list_,\n              parabolic_system_->subsection());\n\n      const auto populate = [this]() {\n        bool initialized = false;\n        for (auto &it : electrostatic_configuration_list_)\n\n          if (it->name() == parabolic_system_->electrostatic_configuration()) {\n            selected_electrostatic_configuration_ = it;\n            initialized = true;\n            break;\n          }\n\n        AssertThrow(initialized,\n                    dealii::ExcMessage(\n                        \"Could not find an electrostatic configuration \"\n                        \"description with name \\\"\" +\n                        parabolic_system_->electrostatic_configuration() +\n                        \"\\\"\"));\n      };\n\n      ParameterAcceptor::parse_parameters_call_back.connect(populate);\n      populate();\n    }\n\n\n    template <typename Description, int dim, typename Number>\n    void ParabolicModule<Description, dim, Number>::prepare()\n    {\n#ifdef DEBUG_OUTPUT\n      std::cout << \"ParabolicModule<dim, Number>::prepare()\" << std::endl;\n#endif\n      /*\n       * The cycle_ variabe is only used for gmg reinitialization, simply\n       * reset it to zero on prepare().\n       */\n      cycle_ = 0;\n\n      const auto &discretization = offline_data_->discretization();\n      AssertThrow(discretization.ansatz() == Ansatz::dg_q1 ||\n                      discretization.ansatz() == Ansatz::cg_q1,\n                  dealii::ExcMessage(\"The Euler-Poisson module currently only \"\n                                     \"supports cG/dg Q1 finite elements.\"));\n\n      AssertThrow(!offline_data_->dof_handler().has_hp_capabilities(),\n                  dealii::ExcMessage(\n                      \"The Euler-Poisson module currently does not support \"\n                      \"DoFHandlers set up with hp capabilities.\"));\n\n      potential_initialized_ = false;\n\n      /*\n       * (Re)initialize matrix free object:\n       */\n\n      typename MatrixFree<dim, Number>::AdditionalData additional_data;\n      additional_data.tasks_parallel_scheme =\n          MatrixFree<dim, Number>::AdditionalData::none;\n\n      // First index CG, second index hyperbolic ansatz\n      std::vector<const dealii::DoFHandler<dim> *> dof_handlers = {\n          &offline_data_->dof_handler_cg(), &offline_data_->dof_handler()};\n\n      create_constraints();\n      std::vector<const dealii::AffineConstraints<Number> *>\n          affine_constraints = {&affine_constraints_potential_,\n                                &offline_data_->affine_constraints()};\n\n      // First index full quadrature, second index lumped quadrature\n      std::vector<dealii::Quadrature<1>> quadratures = {\n          discretization.quadrature_1d()[0],\n          discretization.nodal_quadrature_1d()[0]};\n\n      matrix_free_.reinit(discretization.mapping(),\n                          dof_handlers,\n                          affine_constraints,\n                          quadratures,\n                          additional_data);\n\n      /*\n       * (Re)initialize operators and preconditioners:\n       */\n\n      laplace_operator_.initialize(matrix_free_);\n      laplace_operator_.compute_diagonal(diagonal_preconditioner_);\n      update_operator_.initialize(matrix_free_, density_, magnetic_field_);\n\n      typename decltype(multigrid_preconditioner_)::MultigridParameters\n          parameters{gmg_max_iter_,\n                     gmg_smoother_range_,\n                     gmg_smoother_max_eig_,\n                     gmg_smoother_degree_,\n                     gmg_smoother_n_cg_iter_,\n                     gmg_min_level_,\n                     tolerance_};\n\n      multigrid_preconditioner_.initialize(\n          *offline_data_,\n          selected_electrostatic_configuration_->dirichlet_boundaries(),\n          parameters);\n\n      /*\n       * (Re)initialize auxiliary vectors:\n       */\n\n      const auto &potential_partitioner =\n          matrix_free_.get_dof_info(0).vector_partitioner;\n      potential_rhs_.reinit(potential_partitioner);\n\n      const auto &scalar_partitioner =\n          matrix_free_.get_dof_info(1).vector_partitioner;\n      density_.reinit(scalar_partitioner);\n      background_density_.reinit(scalar_partitioner);\n\n      magnetic_field_.reinit(dim == 2 ? 1 : dim);\n      for (unsigned int i = 0; i < magnetic_field_.n_blocks(); ++i)\n        magnetic_field_.block(i).reinit(scalar_partitioner);\n\n      velocity_rhs_.reinit(dim);\n      for (unsigned int i = 0; i < dim; ++i)\n        velocity_rhs_.block(i).reinit(scalar_partitioner);\n\n      /*\n       * Populate background fields:\n       */\n\n      if (!selected_electrostatic_configuration_->is_time_dependent()) {\n        update_background_density(Number(0.));\n        update_magnetic_field(Number(0.));\n      }\n    }\n\n\n    template <typename Description, int dim, typename Number>\n    void ParabolicModule<Description, dim, Number>::reinit_state_vector(\n        StateVector &state_vector) const\n    {\n#ifdef DEBUG_OUTPUT\n      std::cout << \"ParabolicModule<dim, Number>::reinit_state_vector()\"\n                << std::endl;\n#endif\n\n      auto &[U, precomputed, V] = state_vector;\n      V.reinit(1);\n\n      auto &potential = V.block(0);\n      const auto &partitioner = matrix_free_.get_dof_info(0).vector_partitioner;\n      potential.reinit(partitioner);\n      potential = 0.;\n    }\n\n\n    template <typename Description, int dim, typename Number>\n    void ParabolicModule<Description, dim, Number>::prepare_state_vector(\n        StateVector &state_vector, Number t) const\n    {\n#ifdef DEBUG_OUTPUT\n      std::cout << \"ParabolicModule<dim, Number>::prepare_state_vector()\"\n                << std::endl;\n#endif\n\n      /*\n       * We (re)compute the potential on the first step and if the restart\n       * strategy is set to full_restart or static_full_restart.\n       */\n\n      AssertThrow(gauss_law_restart_strategy_ !=\n                      GaussLawRestartStrategy::correction,\n                  dealii::ExcNotImplemented());\n\n      if (!potential_initialized_ ||\n          (gauss_law_restart_strategy_ ==\n           GaussLawRestartStrategy::full_restart) ||\n          (gauss_law_restart_strategy_ ==\n           GaussLawRestartStrategy::static_full_restart)) {\n\n        compute_potential(t, state_vector);\n\n        if (!potential_initialized_ &&\n            parabolic_system_->magnetic_drift_limit())\n          enforce_magnetic_drift_velocity(state_vector);\n        potential_initialized_ = true;\n      }\n    }\n\n\n    template <typename Description, int dim, typename Number>\n    template <int stages>\n    void ParabolicModule<Description, dim, Number>::backward_euler_step(\n        const StateVector &old_state_vector,\n        const Number old_t,\n        std::array<std::reference_wrapper<const StateVector>,\n                   stages> /*stage_state_vectors*/,\n        const std::array<Number, stages> /*stage_weights*/,\n        StateVector &new_state_vector,\n        Number tau) const\n    {\n      step(old_state_vector,\n           old_t,\n           new_state_vector,\n           tau,\n           /*crank_nicolson_extrapolation = */ false);\n    }\n\n\n    template <typename Description, int dim, typename Number>\n    void ParabolicModule<Description, dim, Number>::crank_nicolson_step(\n        const StateVector &old_state_vector,\n        const Number old_t,\n        StateVector &new_state_vector,\n        Number tau) const\n    {\n      try {\n        /* Backward Euler step to half time step, and extrapolate: */\n\n        step(old_state_vector,\n             old_t,\n             new_state_vector,\n             tau / Number(2.),\n             /*crank_nicolson_extrapolation = */ true);\n\n      } catch (Correction) {\n\n        /*\n         * Under very rare circumstances we might fail to perform a Crank\n         * Nicolson step because the extrapolation step produced\n         * inadmissible states. We could correct the update now by\n         * performing a limiting step (either convex limiting, or flux\n         * corrected transport)... but *meh*, just perform a backward Euler\n         * step:\n         */\n        step(old_state_vector,\n             old_t,\n             new_state_vector,\n             tau,\n             /*crank_nicolson_extrapolation = */ false);\n      }\n    }\n\n\n    template <typename Description, int dim, typename Number>\n    void ParabolicModule<Description, dim, Number>::create_constraints()\n    {\n#ifdef DEBUG_OUTPUT\n      std::cout << \"ParabolicModule<dim, Number>::create_constraints()\"\n                << std::endl;\n#endif\n\n      const auto &discretization = offline_data_->discretization();\n      const auto &dof_handler = offline_data_->dof_handler_cg();\n\n      affine_constraints_potential_.clear();\n\n#if DEAL_II_VERSION_GTE(9, 6, 0)\n      const auto locally_relevant =\n          DoFTools::extract_locally_relevant_dofs(dof_handler);\n#else\n      IndexSet locally_relevant;\n      DoFTools::extract_locally_relevant_dofs(dof_handler, locally_relevant);\n#endif\n\n#if DEAL_II_VERSION_GTE(9, 6, 0)\n      const IndexSet &locally_owned = dof_handler.locally_owned_dofs();\n      affine_constraints_potential_.reinit(locally_owned, locally_relevant);\n#else\n      affine_constraints_potential_.reinit(locally_relevant);\n#endif\n\n      DoFTools::make_hanging_node_constraints(offline_data_->dof_handler_cg(),\n                                              affine_constraints_potential_);\n\n      /*\n       * Enforce periodic boundary conditions. We assume that the mesh is in\n       * \"normal configuration.\"\n       */\n\n      const auto &periodic_faces =\n          discretization.triangulation().get_periodic_face_map();\n\n      for (const auto &[left, value] : periodic_faces) {\n        const auto &[right, orientation] = value;\n\n        typename DoFHandler<dim>::cell_iterator dof_cell_left(\n            &left.first->get_triangulation(),\n            left.first->level(),\n            left.first->index(),\n            &dof_handler);\n\n        typename DoFHandler<dim>::cell_iterator dof_cell_right(\n            &right.first->get_triangulation(),\n            right.first->level(),\n            right.first->index(),\n            &dof_handler);\n\n        if constexpr (std::is_same_v<Number, double>) {\n          DoFTools::make_periodicity_constraints(\n              dof_cell_left->face(left.second),\n              dof_cell_right->face(right.second),\n              affine_constraints_potential_,\n              ComponentMask(),\n#if DEAL_II_VERSION_GTE(9, 6, 0)\n              orientation);\n#else\n              /* orientation */ orientation[0],\n              /* flip */ orientation[1],\n              /* rotation */ orientation[2]);\n#endif\n        } else {\n          AssertThrow(false, dealii::ExcNotImplemented());\n          __builtin_trap();\n        }\n      }\n\n      for (const auto &it :\n           selected_electrostatic_configuration_->dirichlet_boundaries())\n        DoFTools::make_zero_boundary_constraints(\n            offline_data_->dof_handler_cg(), it, affine_constraints_potential_);\n\n      affine_constraints_potential_.close();\n    }\n\n\n    template <typename Description, int dim, typename Number>\n    void ParabolicModule<Description, dim, Number>::update_background_density(\n        const Number t) const\n    {\n#ifdef DEBUG_OUTPUT\n      std::cout << \"ParabolicModule<dim, Number>::update_background_density()\"\n                << std::endl;\n#endif\n\n      /*\n       * Skip updating the background density if t > 0 and if the fields\n       * are time independent:\n       */\n      if (!selected_electrostatic_configuration_->is_time_dependent() &&\n          (t > Number(0.)))\n        return;\n\n      /*\n       * Skip updating if we have already populated the background density\n       * for the chosen time t.\n       */\n      if (std::abs(t_background_density_ - t) < 1.e-12)\n        return;\n\n#ifdef DEBUG_OUTPUT\n      std::cout << \"        updating to t = \" << t << std::endl;\n#endif\n\n      Scope scope(computing_timer_,\n                  \"time step [X]   - interpolate data vectors\");\n\n      const auto &discretization = offline_data_->discretization();\n      background_density_.zero_out_ghost_values();\n      dealii::VectorTools::interpolate(\n          discretization.mapping(),\n          offline_data_->dof_handler(),\n          dealii::ScalarFunctionFromFunctionObject<dim, Number>(\n              [&](const dealii::Point<dim> &p) {\n                return selected_electrostatic_configuration_\n                    ->background_density(p, t);\n              }),\n          background_density_);\n      background_density_.update_ghost_values();\n\n      t_background_density_ = t;\n    }\n\n\n    template <typename Description, int dim, typename Number>\n    void ParabolicModule<Description, dim, Number>::update_magnetic_field(\n        const Number t) const\n    {\n#ifdef DEBUG_OUTPUT\n      std::cout << \"ParabolicModule<dim, Number>::update_magnetic_field()\"\n                << std::endl;\n#endif\n\n      /*\n       * Skip updating the background density if t > 0 and if the fields\n       * are time independent:\n       */\n      if (!selected_electrostatic_configuration_->is_time_dependent() &&\n          (t > Number(0.)))\n        return;\n\n      /*\n       * Skip updating if we have already populated the background density\n       * for the chosen time t.\n       */\n      if (std::abs(t_magnetic_field_ - t) < 1.e-12)\n        return;\n\n#ifdef DEBUG_OUTPUT\n      std::cout << \"        updating to t = \" << t << std::endl;\n#endif\n\n      Scope scope(computing_timer_,\n                  \"time step [X]   - interpolate data vectors\");\n\n      const auto &discretization = offline_data_->discretization();\n      for (unsigned int k = 0; k < (dim == 2 ? 1 : dim); ++k) {\n        magnetic_field_.block(k).zero_out_ghost_values();\n        dealii::VectorTools::interpolate(\n            discretization.mapping(),\n            offline_data_->dof_handler(),\n            to_function<dim, Number>(\n                [&](const dealii::Point<dim> &p) {\n                  return selected_electrostatic_configuration_->magnetic_field(\n                      p, t);\n                },\n                k),\n            magnetic_field_.block(k));\n      }\n      magnetic_field_.update_ghost_values();\n\n      t_magnetic_field_ = t;\n    }\n\n\n    template <typename Description, int dim, typename Number>\n    void ParabolicModule<Description, dim, Number>::compute_potential(\n        const Number t, StateVector &state_vector) const\n    {\n#ifdef DEBUG_OUTPUT\n      std::cout << \"ParabolicModule<dim, Number>::compute_potential()\"\n                << std::endl;\n#endif\n      auto &U = std::get<0>(state_vector);\n      auto &V = std::get<2>(state_vector);\n      auto &potential = V.block(0);\n\n      const unsigned int n_owned = offline_data_->n_locally_owned();\n\n      constexpr unsigned int order_fe = 1;\n      constexpr unsigned int order_quad = 2;\n\n      /*\n       * -----------------------------------------------------------------------\n       * Step 1a: build right hand side for Gauss law\n       * -----------------------------------------------------------------------\n       */\n\n      Scope scope(computing_timer_, \"time step [P] 1 - enforce Gauss law\");\n\n      update_background_density(t);\n\n      const auto body_copy = [&](auto sentinel, unsigned int i) {\n        using T = decltype(sentinel);\n        const auto view = hyperbolic_system_->template view<dim, T>();\n        const auto U_i = U.template read_tensor<T>(i);\n        const auto rho_i = view.density(U_i);\n        write_entry<T>(density_, rho_i, i);\n      };\n\n      cpu_simd_loop<Number>(\n          \"time_step_parabolic_1a\", body_copy, 0, n_owned, n_owned);\n\n      density_.update_ghost_values();\n\n      const auto body_matrix_free = [this](const auto &data,\n                                           auto &dst,\n                                           const auto &src,\n                                           const auto range) {\n        FEEvaluation<dim, order_fe, order_quad, /*components*/ 1, Number>\n            fee_potential(data, /*CG*/ 0, /*lumped quadrature*/ 1);\n        FEEvaluation<dim, order_fe, order_quad, /*components*/ 1, Number>\n            fee_density(data, /*hyperbolic*/ 1, /*lumped quadrature*/ 1);\n        FEEvaluation<dim, order_fe, order_quad, /*components*/ 1, Number>\n            fee_background(data, /*hyperbolic*/ 1, /*lumped quadrature*/ 1);\n\n\n        const Number alpha = parabolic_system_->alpha();\n\n        for (unsigned int cell = range.first; cell < range.second; ++cell) {\n          fee_potential.reinit(cell);\n          fee_density.reinit(cell);\n          fee_background.reinit(cell);\n\n          fee_density.gather_evaluate(src, dealii::EvaluationFlags::values);\n          fee_background.gather_evaluate(background_density_,\n                                         dealii::EvaluationFlags::values);\n\n          for (unsigned int q = 0; q < fee_potential.n_q_points; ++q) {\n            const auto density_q = fee_density.get_value(q);\n            const auto background_q = fee_background.get_value(q);\n\n            const auto value = alpha * (density_q + background_q);\n            fee_potential.submit_value(value, q);\n          }\n          fee_potential.integrate_scatter(dealii::EvaluationFlags::values, dst);\n        }\n      };\n\n      matrix_free_.template cell_loop<ScalarHostVector, ScalarHostVector>(\n          body_matrix_free,\n          potential_rhs_,\n          density_,\n          /*zero destination*/ true);\n\n      /*\n       * -----------------------------------------------------------------------\n       * Step 1b: solve Poisson problem\n       * -----------------------------------------------------------------------\n       */\n\n      matrix_free_.get_affine_constraints(0).distribute(potential);\n      matrix_free_.get_affine_constraints(0).set_zero(potential_rhs_);\n\n      const auto tolerance =\n          (tolerance_linfty_norm_ ? potential_rhs_.linfty_norm()\n                                  : potential_rhs_.l2_norm()) *\n          tolerance_;\n\n      typename dealii::SolverCG<ScalarHostVector>::AdditionalData solver_data;\n\n      try {\n        SolverControl solver_control(gmg_max_iter_, tolerance);\n        dealii::SolverCG<ScalarHostVector> solver(solver_control, solver_data);\n        solver.solve(laplace_operator_,\n                     potential,\n                     potential_rhs_,\n                     multigrid_preconditioner_);\n\n\n        if (potential_initialized_) {\n          /* update exponential moving average */\n          n_iterations_gauss_ =\n              0.9 * n_iterations_gauss_ + 0.1 * solver_control.last_step();\n        } else {\n          n_iterations_gauss_ = solver_control.last_step();\n        }\n\n      } catch (SolverControl::NoConvergence &) {\n        SolverControl solver_control(1000, tolerance);\n        dealii::SolverCG<ScalarHostVector> solver(solver_control, solver_data);\n\n        solver.solve(laplace_operator_,\n                     potential,\n                     potential_rhs_,\n                     diagonal_preconditioner_);\n\n        if (potential_initialized_) {\n          /* update exponential moving average */\n          n_iterations_gauss_ *= 0.9;\n          n_iterations_gauss_ +=\n              0.1 * gmg_max_iter_ + 0.1 * solver_control.last_step();\n        } else {\n          n_iterations_gauss_ = gmg_max_iter_ + solver_control.last_step();\n        }\n\n        /* update exponential moving average, counting also GMG iterations */\n      }\n\n      matrix_free_.get_affine_constraints(0).distribute(potential);\n    }\n\n\n    template <typename Description, int dim, typename Number>\n    void\n    ParabolicModule<Description, dim, Number>::enforce_magnetic_drift_velocity(\n        StateVector &state_vector) const\n    {\n#ifdef DEBUG_OUTPUT\n      std::cout\n          << \"ParabolicModule<dim, Number>::enforce_magnetic_drift_velocity()\"\n          << std::endl;\n#endif\n\n      auto &U = std::get<0>(state_vector);\n      auto &V = std::get<2>(state_vector);\n      auto &potential = V.block(0);\n\n      const unsigned int n_owned = offline_data_->n_locally_owned();\n\n      const auto &lumped_mass_matrix_inverse =\n          offline_data_->lumped_mass_matrix_inverse();\n\n      constexpr unsigned int order_fe = 1;\n      constexpr unsigned int order_quad = 2;\n\n      /*\n       * -----------------------------------------------------------------------\n       * Step 1c: enforce magnetic drift velocity\n       * -----------------------------------------------------------------------\n       */\n\n      update_magnetic_field(Number(0.));\n\n      /* Project gradient of potential into velocity space: */\n\n      const auto body_velocity =\n          [](const auto &data, auto &dst, const auto &src, const auto range) {\n            FEEvaluation<dim, order_fe, order_quad, /*components*/ 1, Number>\n                fee_pot(data, /*CG*/ 0, /*lumped quadrature*/ 1);\n            FEEvaluation<dim, order_fe, order_quad, /*components*/ dim, Number>\n                fee_vel(data, /*hyperbolic*/ 1, /*lumped quadrature*/ 1);\n\n            for (unsigned int cell = range.first; cell < range.second; ++cell) {\n              fee_pot.reinit(cell);\n              fee_vel.reinit(cell);\n\n              fee_pot.gather_evaluate(src, dealii::EvaluationFlags::gradients);\n              for (unsigned int q = 0; q < fee_pot.n_q_points; ++q) {\n                fee_vel.submit_value(fee_pot.get_gradient(q), q);\n              }\n              fee_vel.integrate_scatter(dealii::EvaluationFlags::values, dst);\n            }\n          };\n\n      matrix_free_.template cell_loop<BlockHostVector, ScalarHostVector>(\n          body_velocity,\n          velocity_rhs_,\n          potential,\n          /*zero destination*/ true);\n\n      const auto body = [&](auto sentinel, unsigned int i) {\n        using T = decltype(sentinel);\n        const auto view = hyperbolic_system_->template view<dim, T>();\n\n        const auto m_i_inv =\n            lumped_mass_matrix_inverse.template read_entry<T>(i);\n\n        auto U_i = U.template read_tensor<T>(i);\n        const auto rho_i = view.density(U_i);\n        const auto m_i = view.momentum(U_i);\n        const auto v_i = m_i / rho_i;\n\n        dealii::Tensor<1, (dim == 2 ? 1 : dim), T> magnetic_field;\n        for (unsigned int d = 0; d < (dim == 2 ? 1 : dim); ++d)\n          magnetic_field[d] = read_entry<T>(magnetic_field_.block(d), i);\n\n        dealii::Tensor<1, dim, T> grad_phi;\n        for (unsigned int d = 0; d < dim; ++d)\n          grad_phi[d] = m_i_inv * read_entry<T>(velocity_rhs_.block(d), i);\n\n        auto new_v_i = v_i;\n\n        if constexpr (dim == 2) {\n          new_v_i = -magnetic_field[0] * cross_product_2d(grad_phi) /\n                    magnetic_field.norm_square();\n\n        } else if constexpr (dim == 3) {\n          new_v_i = -cross_product_3d(grad_phi, magnetic_field) /\n                    magnetic_field.norm_square();\n        }\n\n        for (unsigned int d = 0; d < dim; ++d)\n          U_i[1 + d] = rho_i * new_v_i[d];\n\n        /* Update the total energy accordingly: */\n        if constexpr (view.have_energy_equation)\n          U_i[1 + dim] +=\n              Number(0.5) * rho_i * (new_v_i.norm_square() - v_i.norm_square());\n\n        U.template write_tensor<T>(U_i, i);\n      };\n\n      cpu_simd_loop<Number>(\n          \"time_step_parabolic_1c\", body, 0, n_owned, n_owned);\n    }\n\n\n    template <typename Description, int dim, typename Number>\n    void ParabolicModule<Description, dim, Number>::step(\n        const StateVector &old_state_vector,\n        const Number t,\n        StateVector &new_state_vector,\n        Number tau [[maybe_unused]],\n        const bool crank_nicolson_extrapolation [[maybe_unused]]) const\n    {\n#ifdef DEBUG_OUTPUT\n      std::cout << \"ParabolicModule<dim, Number>::step()\" << std::endl;\n      std::cout << \"        perform time-step with tau = \" << tau << std::endl;\n      if (crank_nicolson_extrapolation)\n        std::cout << \"        and extrapolate to t + 2 * tau\" << std::endl;\n#endif\n\n      const Number alpha = parabolic_system_->alpha();\n\n      const auto &old_U = std::get<0>(old_state_vector);\n      const auto &old_V = std::get<2>(old_state_vector);\n      const auto &old_potential = old_V.block(0);\n\n      auto &new_U = std::get<0>(new_state_vector);\n      auto &new_V = std::get<2>(new_state_vector);\n      auto &new_potential = new_V.block(0);\n\n      const unsigned int n_owned = offline_data_->n_locally_owned();\n\n      const auto &lumped_mass_matrix_inverse =\n          offline_data_->lumped_mass_matrix_inverse();\n\n      constexpr unsigned int order_fe = 1;\n      constexpr unsigned int order_quad = 2;\n\n      /*\n       * Initialize the new potential with the old one:\n       */\n\n      new_potential = old_potential;\n\n      /*\n       * If the Gauss law restart strategy is \"static full restart\" or\n       * \"static no restart\", we skip updating the potential.\n       */\n      if ((gauss_law_restart_strategy_ !=\n           GaussLawRestartStrategy::static_no_restart) &&\n          (gauss_law_restart_strategy_ !=\n           GaussLawRestartStrategy::static_full_restart)) {\n\n        /*\n         * ---------------------------------------------------------------------\n         * Step 2a: build right hand side for potential update\n         *\n         * The right-hand side reads:\n         *   (\\nabla \\varphi^n, \\nabla \\chi) +\n         *   \\tau \\alpha \\langle \\rho^n B^{-1} v^n, \\nabla \\chi \\rangle\n         *\n         * In case of a time-dependent background density, we add a term\n         * \\theta \\alpha \\langle \\rho_b^{n+1} - \\rho_b^n, \\chi \\rangle to\n         * account for the time dependence. Here, t_{n+1} is the final time\n         * t_n + tau, or t_n + 2 * tau (in case of Crank Nicolson). This\n         * ensures that we are consistent with the Gauß law involution\n         * \"-\\Delta \\varphi^{n+1} = \\alpha \\rho^{n+1}.\"\n         * ---------------------------------------------------------------------\n         */\n\n        Scope scope(computing_timer_, \"time step [P] 2 - update potential\");\n\n        /* Query the magnetic field at the time t + tau: */\n        update_magnetic_field(t + tau);\n\n        /*\n         * Write out density and assemble velocity part. We need density_\n         * to be set to the correct density for UpdateOperator::vmult()\n         */\n\n        const auto body_copy = [&](auto sentinel, unsigned int i) {\n          using T = decltype(sentinel);\n          const auto view = hyperbolic_system_->template view<dim, T>();\n\n          const auto U_i = old_U.template read_tensor<T>(i);\n          const auto rho_i = view.density(U_i);\n          const auto m_i = view.momentum(U_i);\n\n          dealii::Tensor<1, (dim == 2 ? 1 : dim), T> magnetic_field;\n          for (unsigned int d = 0; d < (dim == 2 ? 1 : dim); ++d)\n            magnetic_field[d] = read_entry<T>(magnetic_field_.block(d), i);\n\n          const auto velocity_rhs =\n              tau * alpha * rho_i *\n              apply_B_n_inverse(magnetic_field, tau, m_i / rho_i);\n\n          write_entry<T>(density_, rho_i, i);\n          for (unsigned int d = 0; d < dim; ++d)\n            write_entry<T>(velocity_rhs_.block(d), velocity_rhs[d], i);\n        };\n\n        cpu_simd_loop<Number>(\n            \"time_step_parabolic_2a\", body_copy, 0, n_owned, n_owned);\n\n        density_.update_ghost_values();\n\n        /* Apply Laplace operator to right hand side: */\n\n        const auto body_laplace = [](const auto &data,\n                                     auto &dst,\n                                     const auto &src,\n                                     const auto range) {\n          FEEvaluation<dim, order_fe, order_quad, /*components*/ 1, Number> fee(\n              data, /*CG*/ 0, /*full quadrature*/ 0);\n\n          for (unsigned int cell = range.first; cell < range.second; ++cell) {\n            fee.reinit(cell);\n            fee.gather_evaluate(src, dealii::EvaluationFlags::gradients);\n\n            for (unsigned int q = 0; q < fee.n_q_points; ++q) {\n              const auto grad_potential = fee.get_gradient(q);\n              fee.submit_gradient(grad_potential, q);\n            }\n            fee.integrate_scatter(dealii::EvaluationFlags::gradients, dst);\n          }\n        };\n\n        matrix_free_.template cell_loop<ScalarHostVector, ScalarHostVector>(\n            body_laplace,\n            potential_rhs_,\n            old_potential,\n            /*zero destination*/ true);\n\n        /* Apply Velocity contribution to right hand side: */\n\n        const auto body_velocity = [](const auto &data,\n                                      auto &dst,\n                                      const auto &src,\n                                      const auto range) {\n          FEEvaluation<dim, order_fe, order_quad, /*components*/ 1, Number>\n              fee_pot(data, /*CG*/ 0, /*lumped quadrature*/ 1);\n          FEEvaluation<dim, order_fe, order_quad, /*components*/ dim, Number>\n              fee_vel(data, /*hyperbolic*/ 1, /*lumped quadrature*/ 1);\n\n          for (unsigned int cell = range.first; cell < range.second; ++cell) {\n            fee_pot.reinit(cell);\n            fee_vel.reinit(cell);\n\n            fee_vel.gather_evaluate(src, dealii::EvaluationFlags::values);\n\n            for (unsigned int q = 0; q < fee_pot.n_q_points; ++q) {\n              if constexpr (dim == 1) {\n                decltype(fee_pot.get_gradient(q)) velocity_rhs;\n                velocity_rhs[0] = fee_vel.get_value(q);\n                fee_pot.submit_gradient(velocity_rhs, q);\n              } else {\n                fee_pot.submit_gradient(fee_vel.get_value(q), q);\n              }\n            }\n            fee_pot.integrate_scatter(dealii::EvaluationFlags::gradients, dst);\n          }\n        };\n\n        matrix_free_.template cell_loop<ScalarHostVector, BlockHostVector>(\n            body_velocity,\n            potential_rhs_,\n            velocity_rhs_,\n            /*zero destination*/ false);\n\n        /* Time-dependent background density: */\n\n        if (selected_electrostatic_configuration_->is_time_dependent()) {\n\n          /*\n           * Subtract background density at time t_n:\n           */\n\n          update_background_density(t);\n\n          Number factor = (crank_nicolson_extrapolation ? -0.5 : -1.0) * alpha;\n\n          const auto body = [&factor](const auto &data,\n                                      auto &dst,\n                                      const auto &src,\n                                      const auto range) {\n            FEEvaluation<dim, order_fe, order_quad, /*components*/ 1, Number>\n                fee_potential(data, /*CG*/ 0, /*lumped quadrature*/ 1);\n            FEEvaluation<dim, order_fe, order_quad, /*components*/ 1, Number>\n                fee_background(data, /*hyperbolic*/ 1, /*lumped quadrature*/ 1);\n\n            for (unsigned int cell = range.first; cell < range.second; ++cell) {\n              fee_potential.reinit(cell);\n              fee_background.reinit(cell);\n              fee_background.gather_evaluate(src, EvaluationFlags::values);\n\n              for (unsigned int q = 0; q < fee_potential.n_q_points; ++q) {\n                const auto background_q = fee_background.get_value(q);\n                fee_potential.submit_value(factor * background_q, q);\n              }\n              fee_potential.integrate_scatter(EvaluationFlags::values, dst);\n            }\n          };\n\n          matrix_free_.template cell_loop<ScalarHostVector, ScalarHostVector>(\n              body,\n              potential_rhs_,\n              background_density_,\n              /*zero destination*/ false);\n\n          /*\n           * Add background density at time t_{n+1}:\n           */\n\n          update_background_density(\n              t + (crank_nicolson_extrapolation ? 2. : 1.) * tau);\n\n          factor *= -1.;\n\n          matrix_free_.template cell_loop<ScalarHostVector, ScalarHostVector>(\n              body,\n              potential_rhs_,\n              background_density_,\n              /*zero destination*/ false);\n        }\n\n        /*\n         * ---------------------------------------------------------------------\n         * Step 2b: solve modified poisson problem\n         * ---------------------------------------------------------------------\n         */\n\n        update_operator_.set_alpha(alpha);\n        update_operator_.set_theta_tau(tau);\n\n        matrix_free_.get_affine_constraints(0).distribute(new_potential);\n        matrix_free_.get_affine_constraints(0).set_zero(potential_rhs_);\n\n        const auto tolerance =\n            (tolerance_linfty_norm_ ? potential_rhs_.linfty_norm()\n                                    : potential_rhs_.l2_norm()) *\n            tolerance_;\n\n        typename dealii::SolverCG<ScalarHostVector>::AdditionalData solver_data;\n\n        try {\n          SolverControl solver_control(gmg_max_iter_, tolerance);\n          dealii::SolverCG<ScalarHostVector> solver(solver_control,\n                                                    solver_data);\n          solver.solve(update_operator_,\n                       new_potential,\n                       potential_rhs_,\n                       multigrid_preconditioner_);\n\n          /* update exponential moving average */\n          n_iterations_step_ =\n              0.9 * n_iterations_step_ + 0.1 * solver_control.last_step();\n\n        } catch (SolverControl::NoConvergence &) {\n          SolverControl solver_control(1000, tolerance);\n          dealii::SolverCG<ScalarHostVector> solver(solver_control,\n                                                    solver_data);\n\n          solver.solve(update_operator_,\n                       new_potential,\n                       potential_rhs_,\n                       diagonal_preconditioner_);\n\n          /* update exponential moving average, counting also GMG iterations */\n          n_iterations_step_ *= 0.9;\n          n_iterations_step_ +=\n              0.1 * gmg_max_iter_ + 0.1 * solver_control.last_step();\n        }\n\n        matrix_free_.get_affine_constraints(0).distribute(new_potential);\n      }\n\n      /*\n       * ---------------------------------------------------------------------\n       * Step 2c: update velocity vector field; Crank-Nicolson extrapolation\n       * ---------------------------------------------------------------------\n       */\n\n      /* Project gradient of potential into velocity space: */\n\n      const auto body_velocity =\n          [](const auto &data, auto &dst, const auto &src, const auto range) {\n            FEEvaluation<dim, order_fe, order_quad, /*components*/ 1, Number>\n                fee_pot(data, /*CG*/ 0, /*lumped quadrature*/ 1);\n            FEEvaluation<dim, order_fe, order_quad, /*components*/ dim, Number>\n                fee_vel(data, /*hyperbolic*/ 1, /*lumped quadrature*/ 1);\n\n            for (unsigned int cell = range.first; cell < range.second; ++cell) {\n              fee_pot.reinit(cell);\n              fee_vel.reinit(cell);\n\n              fee_pot.gather_evaluate(src, dealii::EvaluationFlags::gradients);\n              for (unsigned int q = 0; q < fee_pot.n_q_points; ++q) {\n                fee_vel.submit_value(fee_pot.get_gradient(q), q);\n              }\n              fee_vel.integrate_scatter(dealii::EvaluationFlags::values, dst);\n            }\n          };\n\n      matrix_free_.template cell_loop<BlockHostVector, ScalarHostVector>(\n          body_velocity,\n          velocity_rhs_,\n          new_potential,\n          /*zero destination*/ true);\n\n      /*\n       * Now that we have written out the gradients, copy over the old\n       * state vector and perform the Crank-Nicolson extrapolation step on\n       * the potential:\n       */\n\n      new_U = old_U;\n\n      if (crank_nicolson_extrapolation) {\n        new_potential *= Number(2.);\n        new_potential -= old_potential;\n      }\n\n      /*\n       * Update the momentum and total energy:\n       */\n\n      const auto body = [&](auto sentinel, unsigned int i) {\n        using T = decltype(sentinel);\n        const auto view = hyperbolic_system_->template view<dim, T>();\n\n        const auto m_i_inv =\n            lumped_mass_matrix_inverse.template read_entry<T>(i);\n\n        const auto old_U_i = old_U.template read_tensor<T>(i);\n        const auto rho_i = view.density(old_U_i);\n        const auto old_m_i = view.momentum(old_U_i);\n        const auto old_v_i = old_m_i / rho_i;\n\n        dealii::Tensor<1, (dim == 2 ? 1 : dim), T> magnetic_field;\n        for (unsigned int d = 0; d < (dim == 2 ? 1 : dim); ++d)\n          magnetic_field[d] = read_entry<T>(magnetic_field_.block(d), i);\n\n        dealii::Tensor<1, dim, T> grad_phi;\n        for (unsigned int d = 0; d < dim; ++d)\n          grad_phi[d] = m_i_inv * read_entry<T>(velocity_rhs_.block(d), i);\n\n        auto new_v_i =\n            apply_B_n_inverse(magnetic_field, tau, old_v_i - tau * grad_phi);\n\n        /* Perform an extrapolation step: */\n        if (crank_nicolson_extrapolation)\n          new_v_i = Number(2.) * new_v_i - old_v_i;\n\n        auto new_U_i = old_U_i;\n        for (unsigned int d = 0; d < dim; ++d)\n          new_U_i[1 + d] = rho_i * new_v_i[d];\n\n        /* Update the total energy accordingly: */\n        if constexpr (view.have_energy_equation)\n          new_U_i[1 + dim] += Number(0.5) * rho_i *\n                              (new_v_i.norm_square() - old_v_i.norm_square());\n\n        new_U.template write_tensor<T>(new_U_i, i);\n      };\n\n      cpu_simd_loop<Number>(\n          \"time_step_parabolic_2c\", body, 0, n_owned, n_owned);\n    }\n\n\n    template <typename Description, int dim, typename Number>\n    void ParabolicModule<Description, dim, Number>::print_solver_statistics(\n        std::ostream &output) const\n    {\n      output << \"        [ \" << std::setprecision(2) << std::fixed //\n             << n_iterations_gauss_ << \" GMG gauss -- \"            //\n             << n_iterations_step_ << \" GMG step ]\" << std::endl;\n    }\n\n  } // namespace EulerPoisson\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/euler_poisson/parabolic_system.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n#include <convenience_macros.h>\n\n#include <deal.II/base/function_parser.h>\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/tensor.h>\n\n\nnamespace ryujin\n{\n  namespace EulerPoisson\n  {\n    class ParabolicSystem final : public dealii::ParameterAcceptor\n    {\n    public:\n      static inline const std::string problem_name =\n          \"Electrostatic force model with background magnetic field\";\n\n      static constexpr bool is_identity = false;\n\n      ParabolicSystem(const std::string &subsection = \"/ParabolicSystem\");\n\n      ACCESSOR_READ_ONLY(alpha)\n\n      ACCESSOR_READ_ONLY(magnetic_drift_limit);\n\n      ACCESSOR_READ_ONLY(electrostatic_configuration);\n\n      ACCESSOR_READ_ONLY(subsection);\n\n      unsigned int n_parabolic_state_vectors() const\n      {\n        return parabolic_component_names_.size();\n      }\n\n\n      ACCESSOR_READ_ONLY(parabolic_component_names);\n\n    private:\n      double alpha_;\n      bool magnetic_drift_limit_;\n      std::string subsection_;\n\n      std::string electrostatic_configuration_;\n\n      static inline const std::vector<std::string> parabolic_component_names_ =\n          {\"phi\"};\n    };\n\n\n    inline ParabolicSystem::ParabolicSystem(const std::string &subsection)\n        : ParameterAcceptor(subsection)\n        , subsection_(subsection)\n    {\n      alpha_ = 1.0;\n      add_parameter(\"alpha\", alpha_, \"The coupling constant alpha\");\n\n      magnetic_drift_limit_ = false;\n      add_parameter(\"set up magnetic drift limit\",\n                    magnetic_drift_limit_,\n                    \"If set to true, then the velocity field is initialized to \"\n                    \"satisfy the magnetic drift limit.\");\n\n      electrostatic_configuration_ = \"constant\";\n      add_parameter(\n          \"electrostatic configuration\",\n          electrostatic_configuration_,\n          \"Valid names are given by any of the subsections defined below\");\n    }\n\n  } // namespace EulerPoisson\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_poisson_aeos/CMakeLists.txt",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2026 by the ryujin authors\n##\n\nadd_library(obj_euler_poisson_aeos OBJECT\n  equation_dispatch.cc\n  initial_state_library.cc\n  parabolic_module.cc\n  )\nset_target_properties(obj_euler_poisson_aeos PROPERTIES LINKER_LANGUAGE CXX)\ndeal_ii_setup_target(obj_euler_poisson_aeos)\ntarget_link_libraries(obj_euler_poisson_aeos obj_common ${EXTERNAL_TARGETS})\n# Propagate the current source directory with PUBLIC visibility\ntarget_include_directories(obj_euler_poisson_aeos PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})\n"
  },
  {
    "path": "source/euler_poisson_aeos/Makefile",
    "content": "default: all\n.PHONY: default\n\n%:\n\t@cd .. && make $@\n.PHONY: %\n"
  },
  {
    "path": "source/euler_poisson_aeos/description.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include \"../euler_aeos/hyperbolic_system.h\"\n#include \"../euler_aeos/indicator.h\"\n#include \"../euler_aeos/limiter.h\"\n#include \"../euler_aeos/riemann_solver.h\"\n#include \"../euler_poisson/parabolic_module.h\"\n#include \"../euler_poisson/parabolic_system.h\"\n\nnamespace ryujin\n{\n  namespace EulerPoissonAEOS\n  {\n    struct Description {\n      using HyperbolicSystem = EulerAEOS::HyperbolicSystem;\n\n      template <int dim, typename Number = double>\n      using HyperbolicSystemView = EulerAEOS::HyperbolicSystemView<dim, Number>;\n\n      using ParabolicSystem = EulerPoisson::ParabolicSystem;\n\n      template <int dim, typename Number = double>\n      using ParabolicModule =\n          EulerPoisson::ParabolicModule<Description, dim, Number>;\n\n      template <int dim, typename Number = double>\n      using Indicator = EulerAEOS::Indicator<dim, Number>;\n\n      template <int dim, typename Number = double>\n      using Limiter = EulerAEOS::Limiter<dim, Number>;\n\n      template <int dim, typename Number = double>\n      using RiemannSolver = EulerAEOS::RiemannSolver<dim, Number>;\n    };\n  } // namespace EulerPoissonAEOS\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_poisson_aeos/equation_dispatch.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2024 - 2026 by the ryujin authors\n//\n\n#include \"description.h\"\n\n#include <compile_time_options.h>\n#include <equation_dispatch.h>\n\nnamespace ryujin\n{\n  namespace EulerPoissonAEOS\n  {\n    Dispatch<Description, NUMBER> dispatch_instance(\"euler poisson aeos\");\n  } // namespace EulerPoissonAEOS\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_poisson_aeos/initial_state_library.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2026 by the ryujin authors\n//\n\n#include \"initial_state_library.template.h\"\n\nnamespace ryujin\n{\n  template class InitialStateLibrary<Description, 1, NUMBER>;\n  template class InitialStateLibrary<Description, 2, NUMBER>;\n  template class InitialStateLibrary<Description, 3, NUMBER>;\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_poisson_aeos/initial_state_library.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include \"description.h\"\n\n#include \"../euler/initial_state_library_euler.h\"\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  using Description = EulerPoissonAEOS::Description;\n\n  template <int dim, typename Number>\n  class InitialStateLibrary<Description, dim, Number>\n  {\n  public:\n    using HyperbolicSystem = typename Description::HyperbolicSystem;\n    using ParabolicSystem = typename Description::ParabolicSystem;\n\n    using View =\n        typename Description::template HyperbolicSystemView<dim, Number>;\n\n    using initial_state_list_type =\n        std::set<std::unique_ptr<InitialState<Description, dim, Number>>>;\n\n    static void\n    populate_initial_state_list(initial_state_list_type &initial_state_list,\n                                const HyperbolicSystem &h,\n                                const ParabolicSystem & /*p*/,\n                                const std::string &s)\n    {\n      EulerInitialStates::populate_initial_state_list<Description, dim, Number>(\n          initial_state_list, h, s);\n    }\n  };\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_poisson_aeos/instantiate.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2026 by the ryujin authors\n//\n\n#ifndef RYUJIN_INCLUDE_INSTANTIATION_ONCE\n#define RYUJIN_INCLUDE_INSTANTIATION_ONCE\n#else\n#error Instantiation files can only be included once.\n#endif\n\n#include \"description.h\"\n\nnamespace ryujin\n{\n  using EulerPoissonAEOS::Description;\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_poisson_aeos/parabolic_module.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2025 - 2026 by the ryujin authors\n//\n\n#include \"../euler_poisson/parabolic_module.template.h\"\n#include \"description.h\"\n\n#define INSTANTIATE(dim, stages)                                               \\\n  template void                                                                \\\n  ParabolicModule<Description, dim, NUMBER>::backward_euler_step<stages>(      \\\n      const StateVector &,                                                     \\\n      const NUMBER,                                                            \\\n      std::array<std::reference_wrapper<const StateVector>, stages>,           \\\n      const std::array<NUMBER, stages>,                                        \\\n      StateVector &,                                                           \\\n      NUMBER) const\n\nnamespace ryujin\n{\n  namespace EulerPoisson\n  {\n    using EulerPoissonAEOS::Description;\n\n    template class ParabolicModule<Description, 1, NUMBER>;\n    template class ParabolicModule<Description, 2, NUMBER>;\n    template class ParabolicModule<Description, 3, NUMBER>;\n\n    INSTANTIATE(1, 0);\n    INSTANTIATE(1, 1);\n    INSTANTIATE(1, 2);\n    INSTANTIATE(1, 3);\n\n    INSTANTIATE(2, 0);\n    INSTANTIATE(2, 1);\n    INSTANTIATE(2, 2);\n    INSTANTIATE(2, 3);\n\n    INSTANTIATE(3, 0);\n    INSTANTIATE(3, 1);\n    INSTANTIATE(3, 2);\n    INSTANTIATE(3, 3);\n  } // namespace EulerPoisson\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_poisson_barotropic/CMakeLists.txt",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2026 by the ryujin authors\n##\n\nadd_library(obj_euler_poisson_barotropic OBJECT\n  equation_dispatch.cc\n  initial_state_library.cc\n  parabolic_module.cc\n  )\nset_target_properties(obj_euler_poisson_barotropic PROPERTIES LINKER_LANGUAGE CXX)\ndeal_ii_setup_target(obj_euler_poisson_barotropic)\ntarget_link_libraries(obj_euler_poisson_barotropic obj_common ${EXTERNAL_TARGETS})\n# Propagate the current source directory with PUBLIC visibility\ntarget_include_directories(obj_euler_poisson_barotropic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})\n"
  },
  {
    "path": "source/euler_poisson_barotropic/Makefile",
    "content": "default: all\n.PHONY: default\n\n%:\n\t@cd .. && make $@\n.PHONY: %\n"
  },
  {
    "path": "source/euler_poisson_barotropic/description.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include \"../euler_barotropic/hyperbolic_system.h\"\n#include \"../euler_barotropic/indicator.h\"\n#include \"../euler_barotropic/limiter.h\"\n#include \"../euler_barotropic/riemann_solver.h\"\n#include \"../euler_poisson/parabolic_module.h\"\n#include \"../euler_poisson/parabolic_system.h\"\n\nnamespace ryujin\n{\n  namespace EulerPoissonBarotropic\n  {\n    struct Description {\n      using HyperbolicSystem = EulerBarotropic::HyperbolicSystem;\n\n      template <int dim, typename Number = double>\n      using HyperbolicSystemView =\n          EulerBarotropic::HyperbolicSystemView<dim, Number>;\n\n      using ParabolicSystem = EulerPoisson::ParabolicSystem;\n\n      template <int dim, typename Number = double>\n      using ParabolicModule =\n          EulerPoisson::ParabolicModule<Description, dim, Number>;\n\n      template <int dim, typename Number = double>\n      using Indicator = EulerBarotropic::Indicator<dim, Number>;\n\n      template <int dim, typename Number = double>\n      using Limiter = EulerBarotropic::Limiter<dim, Number>;\n\n      template <int dim, typename Number = double>\n      using RiemannSolver = EulerBarotropic::RiemannSolver<dim, Number>;\n    };\n  } // namespace EulerPoissonBarotropic\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_poisson_barotropic/equation_dispatch.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2024 - 2026 by the ryujin authors\n//\n\n#include \"description.h\"\n\n#include <compile_time_options.h>\n#include <equation_dispatch.h>\n\nnamespace ryujin\n{\n  namespace EulerPoissonBarotropic\n  {\n    Dispatch<Description, NUMBER> dispatch_instance(\"euler poisson barotropic\");\n  } // namespace EulerPoissonBarotropic\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_poisson_barotropic/initial_state_library.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2026 by the ryujin authors\n//\n\n#include \"initial_state_library.template.h\"\n\nnamespace ryujin\n{\n  template class InitialStateLibrary<Description, 1, NUMBER>;\n  template class InitialStateLibrary<Description, 2, NUMBER>;\n  template class InitialStateLibrary<Description, 3, NUMBER>;\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_poisson_barotropic/initial_state_library.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include \"description.h\"\n\n#include \"../euler/initial_state_library_euler.h\"\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  using Description = EulerPoissonBarotropic::Description;\n\n  template <int dim, typename Number>\n  class InitialStateLibrary<Description, dim, Number>\n  {\n  public:\n    using HyperbolicSystem = typename Description::HyperbolicSystem;\n    using ParabolicSystem = typename Description::ParabolicSystem;\n\n    using View =\n        typename Description::template HyperbolicSystemView<dim, Number>;\n\n    using initial_state_list_type =\n        std::set<std::unique_ptr<InitialState<Description, dim, Number>>>;\n\n    static void\n    populate_initial_state_list(initial_state_list_type &initial_state_list,\n                                const HyperbolicSystem &h,\n                                const ParabolicSystem & /*p*/,\n                                const std::string &s)\n    {\n      EulerInitialStates::populate_initial_state_list<Description, dim, Number>(\n          initial_state_list, h, s);\n    }\n  };\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_poisson_barotropic/instantiate.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2026 by the ryujin authors\n//\n\n#ifndef RYUJIN_INCLUDE_INSTANTIATION_ONCE\n#define RYUJIN_INCLUDE_INSTANTIATION_ONCE\n#else\n#error Instantiation files can only be included once.\n#endif\n\n#include \"description.h\"\n\nnamespace ryujin\n{\n  using EulerPoissonBarotropic::Description;\n} // namespace ryujin\n"
  },
  {
    "path": "source/euler_poisson_barotropic/parabolic_module.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2025 - 2026 by the ryujin authors\n//\n\n#include \"../euler_poisson/parabolic_module.template.h\"\n#include \"description.h\"\n\n#define INSTANTIATE(dim, stages)                                               \\\n  template void                                                                \\\n  ParabolicModule<Description, dim, NUMBER>::backward_euler_step<stages>(      \\\n      const StateVector &,                                                     \\\n      const NUMBER,                                                            \\\n      std::array<std::reference_wrapper<const StateVector>, stages>,           \\\n      const std::array<NUMBER, stages>,                                        \\\n      StateVector &,                                                           \\\n      NUMBER) const\n\nnamespace ryujin\n{\n  namespace EulerPoisson\n  {\n    using EulerPoissonBarotropic::Description;\n\n    template class ParabolicModule<Description, 1, NUMBER>;\n    template class ParabolicModule<Description, 2, NUMBER>;\n    template class ParabolicModule<Description, 3, NUMBER>;\n\n    INSTANTIATE(1, 0);\n    INSTANTIATE(1, 1);\n    INSTANTIATE(1, 2);\n    INSTANTIATE(1, 3);\n\n    INSTANTIATE(2, 0);\n    INSTANTIATE(2, 1);\n    INSTANTIATE(2, 2);\n    INSTANTIATE(2, 3);\n\n    INSTANTIATE(3, 0);\n    INSTANTIATE(3, 1);\n    INSTANTIATE(3, 2);\n    INSTANTIATE(3, 3);\n  } // namespace EulerPoisson\n} // namespace ryujin\n"
  },
  {
    "path": "source/geometries/Makefile",
    "content": "default: all\n.PHONY: default\n\n%:\n\t@cd .. && make $@\n.PHONY: %\n"
  },
  {
    "path": "source/geometries/geometry.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"convenience_macros.h\"\n\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/dofs/dof_handler.h>\n#include <deal.II/grid/tria.h>\n\n#include <string>\n\nnamespace ryujin\n{\n#ifndef DOXYGEN\n  /* forward declaration */\n  template <int dim>\n  class Discretization;\n#endif\n\n  /**\n   * A small abstract base class to group configuration options for a\n   * number of geometries together.\n   *\n   * @ingroup Mesh\n   */\n  template <int dim>\n  class Geometry : public dealii::ParameterAcceptor\n  {\n  public:\n    /**\n     * Constructor taking geometry name @p name and a subsection @p\n     * subsection as an argument. The dealii::ParameterAcceptor is\n     * initialized with the subsubsection `subsection + \"/\" + name`.\n     */\n    Geometry(const std::string &name, const std::string &subsection)\n        : ParameterAcceptor(subsection + \"/\" + name)\n        , name_(name)\n    {\n    }\n\n    /**\n     * Create a coarse triangulation representing the current Geometry.\n     * This virtual method needs to be implemented in a derived classes.\n     */\n    virtual void create_coarse_triangulation(\n        dealii::Triangulation<dim> &triangulation) const = 0;\n\n    /**\n     * This method is called before we distribute dofs and can be used to\n     * set the correct active FE index for each active cell for the given\n     * DoFHandler, or update material, or manifold ids, etc.\n     *\n     * This method can be left empty for a standard geometry\n     * that only uses only one reference element. The method must be\n     * reimplemented for geometries that use hp capabilities, such as\n     * meshes with mixed finite elements, or meshes with FE_Nothing.\n     */\n    virtual void\n    update_dof_handler(dealii::DoFHandler<dim> & /*dof_handler*/) const\n    {\n    }\n\n    /**\n     * An enum class describing the hp collection\n     */\n    enum class HP_Collection {\n      /*\n       * Instruct the Discretization class to set up standard\n       * continuous/discontinuous Q_k spaces for quarilaterals/hexahedra.\n       */\n      standard_quadrilaterals,\n      /*\n       * Instruct the Discretization class to set up standard\n       * continuous/discontinuous P_k spaces for simplices.\n       */\n      standard_simplices,\n      /*\n       * Inform the Discretization class that the hp::*Collection objects\n       * have already been populated by the Geometry class.\n       */\n      populated_by_geometry\n    };\n\n    /**\n     * Populate all hp::*Collection objects for finite elements, mappings,\n     * and quadratures. As this is a formidable zoo of different collection\n     * objects, we get a writable reference to the discretization object to\n     * set them directly.\n     */\n    virtual HP_Collection populate_hp_collections(\n        const unsigned int /*fe_degree*/,\n        typename ryujin::Discretization<dim>::Collection & /*collection*/) const\n    {\n      /*\n       * Signal, that we did nothing. In this case the Discretization\n       * object will populate all collections with appropriate objects for\n       * the cG Qk, dG Qk finite element on purely quadrilateral, or\n       * hexahedral meshes.\n       */\n      return HP_Collection::standard_quadrilaterals;\n    }\n\n    /**\n     * Return the name of the geometry as (const reference) std::string\n     */\n    ACCESSOR_READ_ONLY(name)\n\n  private:\n    const std::string name_;\n  };\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/geometries/geometry_airfoil.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"geometry_common_includes.h\"\n\n#include \"cubic_spline.h\"\n#include \"transfinite_interpolation.template.h\"\n\nnamespace ryujin\n{\n  namespace\n  {\n    /**\n     * A small helper function to assign values to either a c-style\n     * array (how vertices are stored in deal.II version 9.2 and\n     * earlier), or a std::array (how vertices are stored in deal.II\n     * version 9.3 onwards).\n     */\n    template <typename T, typename T2>\n    inline void assign(T &array,\n                       const std::initializer_list<T2> &initializer_list)\n    {\n      /* accomodate for a possible c-style array... */\n      Assert(std::size(array) == std::size(initializer_list),\n             dealii::ExcMessage(\n                 \"size of initializer list and array does not match\"));\n      std::copy(\n          initializer_list.begin(), initializer_list.end(), std::begin(array));\n    }\n  } // namespace\n\n  namespace Manifolds\n  {\n    /**\n     * @todo Documentation\n     */\n    template <int dim>\n    class AirfoilManifold : public dealii::ChartManifold<dim>\n    {\n      static_assert(dim == 2, \"not implemented for dim != 2\");\n\n    public:\n      AirfoilManifold(const dealii::Point<dim> airfoil_center,\n                      const std::function<double(const double)> &psi_front,\n                      const std::function<double(const double)> &psi_upper,\n                      const std::function<double(const double)> &psi_lower,\n                      const bool upper_side,\n                      const double psi_ratio = 1.0)\n          : airfoil_center(airfoil_center)\n          , psi_front(psi_front)\n          , psi_upper(psi_upper)\n          , psi_lower(psi_lower)\n          , upper_side(upper_side)\n          , ratio_(psi_ratio * psi_front(0.) / psi_front(M_PI))\n          , polar_manifold()\n      {\n        Assert(std::abs(psi_upper(0.) - psi_front(0.5 * M_PI)) < 1.0e-10,\n               dealii::ExcInternalError());\n        Assert(std::abs(psi_lower(0.) + psi_front(1.5 * M_PI)) < 1.0e-10,\n               dealii::ExcInternalError());\n      }\n\n      dealii::Point<dim>\n      pull_back(const dealii::Point<dim> &space_point) const final\n      {\n        auto coordinate = dealii::Point<dim>() + (space_point - airfoil_center);\n\n        /* transform: */\n\n        dealii::Point<dim> chart_point;\n        if (coordinate[0] > 0.) {\n          if (upper_side) {\n            /* upper back airfoil part */\n            chart_point[0] = 1. + coordinate[1] - psi_upper(coordinate[0]);\n            chart_point[1] = 0.5 * M_PI - ratio_ * coordinate[0];\n          } else {\n            /* lower back airfoil part */\n            chart_point[0] = 1. - coordinate[1] + psi_lower(coordinate[0]);\n            chart_point[1] = 1.5 * M_PI + ratio_ * coordinate[0];\n          }\n        } else {\n          /* front part */\n          chart_point = polar_manifold.pull_back(coordinate);\n          chart_point[0] = 1. + chart_point[0] - psi_front(chart_point[1]);\n        }\n\n        return chart_point;\n      }\n\n      dealii::Point<dim>\n      push_forward(const dealii::Point<dim> &point) const final\n      {\n        auto chart_point = point;\n\n        /* transform back */\n\n        dealii::Point<dim> coordinate;\n        if (chart_point[1] < 0.5 * M_PI) {\n          Assert(upper_side, dealii::ExcInternalError());\n          /* upper back airfoil part */\n          coordinate[0] = (0.5 * M_PI - chart_point[1]) / ratio_;\n          Assert(coordinate[0] >= -1.0e-10, dealii::ExcInternalError());\n          coordinate[1] = chart_point[0] - 1. + psi_upper(coordinate[0]);\n        } else if (chart_point[1] > 1.5 * M_PI) {\n          Assert(!upper_side, dealii::ExcInternalError());\n          /* lower back airfoil part */\n          coordinate[0] = (chart_point[1] - 1.5 * M_PI) / ratio_;\n          Assert(coordinate[0] >= -1.0e-10, dealii::ExcInternalError());\n          coordinate[1] = 1. - chart_point[0] + psi_lower(coordinate[0]);\n        } else {\n          /* front part */\n          chart_point[0] = chart_point[0] - 1. + psi_front(chart_point[1]);\n          coordinate = polar_manifold.push_forward(chart_point);\n        }\n\n        return dealii::Point<dim>() + (coordinate + airfoil_center);\n      }\n\n      std::unique_ptr<dealii::Manifold<dim, dim>> clone() const final\n      {\n        const double psi_ratio = ratio_ * psi_front(M_PI) / psi_front(0.);\n        return std::make_unique<AirfoilManifold<dim>>(airfoil_center,\n                                                      psi_front,\n                                                      psi_upper,\n                                                      psi_lower,\n                                                      upper_side,\n                                                      psi_ratio);\n      }\n\n    private:\n      const dealii::Point<dim> airfoil_center;\n      const std::function<double(const double)> psi_front;\n      const std::function<double(const double)> psi_upper;\n      const std::function<double(const double)> psi_lower;\n      const bool upper_side;\n\n      const double ratio_;\n\n      dealii::PolarManifold<dim> polar_manifold;\n    };\n\n\n    /**\n     * @todo Documentation\n     */\n    template <int dim>\n    class GradingManifold : public dealii::ChartManifold<dim>\n    {\n    public:\n      GradingManifold(const dealii::Point<dim> center,\n                      const dealii::Tensor<1, dim> direction,\n                      const double grading,\n                      const double epsilon)\n          : center(center)\n          , direction(direction)\n          , grading(grading)\n          , epsilon(epsilon)\n      {\n      }\n\n      /* FIXME: find out why weights are not normalized. */\n      Point<dim>\n      get_new_point(const ArrayView<const Point<dim>> &surrounding_points,\n                    const ArrayView<const double> &weights) const override\n      {\n        if (weights[0] > 1.0)\n          return surrounding_points[0];\n\n        if (weights[1] > 1.0)\n          return surrounding_points[1];\n\n        return dealii::ChartManifold<dim>::get_new_point(surrounding_points,\n                                                         weights);\n      }\n\n      dealii::Point<dim>\n      pull_back(const dealii::Point<dim> &space_point) const final\n      {\n        auto chart_point = space_point - center;\n\n        for (unsigned int d = 0; d < dim; ++d) {\n          if (std::abs(direction[d]) > 1.0e-10) {\n            const double x = chart_point[d] * std::copysign(1., direction[d]);\n            Assert(x + epsilon > 0, dealii::ExcInternalError());\n            const double x_hat = std::pow(x + epsilon, 1. / grading) -\n                                 std::pow(epsilon, 1. / grading) + 1.e-14;\n            chart_point[d] += (x_hat - x) * std::copysign(1., direction[d]);\n          }\n        }\n\n        return dealii::Point<dim>() + chart_point;\n      }\n\n      dealii::Point<dim>\n      push_forward(const dealii::Point<dim> &chart_point) const final\n      {\n        auto space_point = chart_point;\n\n        for (unsigned int d = 0; d < dim; ++d) {\n          if (std::abs(direction[d]) > 1.0e-10) {\n            const double x_hat =\n                space_point[d] * std::copysign(1., direction[d]);\n            Assert(x_hat + std::pow(epsilon, 1. / grading) > 0,\n                   dealii::ExcInternalError());\n            const double x =\n                std::pow(x_hat + std::pow(epsilon, 1. / grading), grading) -\n                epsilon + 1.e-14;\n            space_point[d] += (x - x_hat) * std::copysign(1., direction[d]);\n          }\n        }\n\n        return center + (space_point - dealii::Point<dim>());\n      }\n\n      std::unique_ptr<dealii::Manifold<dim, dim>> clone() const final\n      {\n        return std::make_unique<GradingManifold<dim>>(\n            center, direction, grading, epsilon);\n      }\n\n    private:\n      const dealii::Point<dim> center;\n      const dealii::Tensor<1, dim> direction;\n      const double grading;\n      const double epsilon;\n    };\n\n\n    /**\n     * @todo Documentation\n     */\n    template <int dim>\n    class ExtrudedManifold : public dealii::Manifold<dim>\n    {\n    public:\n      ExtrudedManifold(const dealii::Manifold<dim - 1> &manifold)\n          : manifold(manifold.clone())\n      {\n      }\n\n      std::unique_ptr<Manifold<dim>> clone() const override\n      {\n        return std::make_unique<ExtrudedManifold<dim>>(*manifold);\n      }\n\n      Point<dim>\n      get_new_point(const ArrayView<const Point<dim>> &surrounding_points,\n                    const ArrayView<const double> &weights) const override\n      {\n        Assert(surrounding_points.size() == weights.size(),\n               dealii::ExcInternalError());\n\n        boost::container::small_vector<dealii::Point<dim - 1>, 100>\n            surrounding_points_projected;\n        std::transform(surrounding_points.begin(),\n                       surrounding_points.end(),\n                       surrounding_points_projected.begin(),\n                       [](const dealii::Point<dim> &source) {\n                         dealii::Point<dim - 1> result;\n                         for (unsigned int d = 0; d < dim - 1; ++d)\n                           result[d] = source[d];\n                         return result;\n                       });\n\n        const auto projected = manifold->get_new_point(\n            ArrayView<const Point<dim - 1>>{surrounding_points_projected.data(),\n                                            weights.size()},\n            weights);\n\n        dealii::Point<dim> result;\n\n        for (unsigned int d = 0; d < dim - 1; ++d)\n          result[d] = projected[d];\n\n        for (unsigned int i = 0; i < weights.size(); ++i)\n          result[dim - 1] += weights[i] * surrounding_points[i][dim - 1];\n\n        return result;\n      }\n\n    private:\n      std::unique_ptr<const dealii::Manifold<dim - 1>> manifold;\n    };\n\n  } // namespace Manifolds\n\n\n  namespace\n  {\n    /**\n     * @todo Documentation\n     */\n    std::array<std::vector<double>, 4>\n    naca_4digit_points(const std::string &serial_number,\n                       const unsigned int n_samples)\n    {\n      AssertThrow(serial_number.size() == 4,\n                  dealii::ExcMessage(\"Invalid NACA 4 digit serial number\"));\n      std::array<unsigned int, 4> digit;\n      std::transform(serial_number.begin(),\n                     serial_number.end(),\n                     digit.begin(),\n                     [](auto it) { return it - '0'; });\n\n      /* thickness */\n      const double t = 0.1 * digit[2] + 0.01 * digit[3];\n      AssertThrow(t > 0.,\n                  dealii::ExcMessage(\"Invalid NACA 4 digit serial number\"));\n\n      /* maximal chamber */\n      const double m = 0.01 * digit[0];\n      /* x position of maximal chamber */\n      const double p = 0.1 * digit[1];\n\n      std::vector<double> x_upper;\n      std::vector<double> y_upper;\n      std::vector<double> x_lower;\n      std::vector<double> y_lower;\n\n      for (unsigned int i = 0; i < n_samples; i++) {\n        const double x = 1. * i / (n_samples - 1);\n        const double y =\n            5. * t *\n            (0.2969 * std::sqrt(x) +\n             x * (-0.126 + x * (-0.3516 + x * (0.2843 + x * (-0.1036)))));\n\n        const double y_c = (x < p) ? m / (p * p) * (2. * p * x - x * x)\n                                   : m / ((1. - p) * (1. - p)) *\n                                         (1. - 2. * p + 2. * p * x - x * x);\n\n        const double dy_c = (x < p) ? 2. * m / (p * p) * (p - x)\n                                    : 2. * m / ((1. - p) * (1. - p)) * (p - x);\n\n        const double theta = std::atan(dy_c);\n\n        x_upper.emplace_back(x - y * std::sin(theta));\n        y_upper.emplace_back(y_c + y * std::cos(theta));\n        x_lower.emplace_back(x + y * std::sin(theta));\n        y_lower.emplace_back(y_c - y * std::cos(theta));\n      }\n\n      /* Fix up roundoff errors: */\n      y_upper.front() = 0.;\n      y_upper.back() = 0.;\n      y_lower.front() = 0.;\n      y_lower.back() = 0.;\n\n      return {{x_upper, y_upper, x_lower, y_lower}};\n    }\n\n\n    /**\n     * The NASA SC(2)-0714 airfoil as described in the technical report\n     *   NASA Technical Paper 2969, 1990\n     *   NASA Supercritical Airfoils, A Matrix of Family-Related Airfoils\n     *   Charles D. Harris\n     *\n     * Table XXI. page 36 (pdf page 38), found here\n     * https://ntrs.nasa.gov/api/citations/19890008197/downloads/19890008197.pdf\n     */\n    std::array<std::vector<double>, 4>\n    nasa_sc2(const std::string &serial_number)\n    {\n      if (serial_number == \"0714\") {\n        std::vector<double> x_upper{\n            .0,  .002, .005, .01, .02, .03, .04, .05, .07, .1,  .12, .15,\n            .17, .2,   .22,  .25, .27, .3,  .33, .35, .38, .4,  .43, .45,\n            .48, .50,  .53,  .55, .57, .6,  .62, .65, .68, .7,  .72, .75,\n            .77, .8,   .82,  .85, .87, .9,  .92, .95, .97, .98, .99, 1.};\n\n        std::vector<double> y_upper{\n            .0,    .0095, .0158, .0219, .0293,  .0343,  .0381,  .0411,\n            .0462, .0518, .0548, .0585, .0606,  .0632,  .0646,  .0664,\n            .0673, .0685, .0692, .0696, .0698,  .0697,  .0695,  .0692,\n            .0684, .0678, .0666, .0656, .0645,  .0625,  .0610,  .0585,\n            .0555, .0533, .0509, .0469, .0439,  .0389,  .0353,  .0294,\n            .0251, .0181, .0131, .0049, -.0009, -.0039, -.0071, -.0104};\n\n        std::vector<double> x_lower{\n            .0,  .002, .005, .01, .02, .03, .04, .05, .07, .1,  .12, .15, .17,\n            .20, .22,  .25,  .28, .3,  .32, .35, .37, .4,  .42, .45, .48, .5,\n            .53, .55,  .58,  .6,  .63, .65, .68, .70, .73, .75, .77, .80, .83,\n            .85, .87,  .89,  .92, .94, .95, .96, .97, .98, .99, 1.};\n\n        std::vector<double> y_lower{\n            .0,     -.0093, -.016,  -.0221, -.0295, -.0344, -.0381, -.0412,\n            -.0462, -.0517, -.0547, -.0585, -.0606, -.0633, -.0647, -.0666,\n            -.068,  -.0687, -.0692, -.0696, -.0696, -.0692, -.0688, -.0676,\n            -.0657, -.0644, -.0614, -.0588, -.0543, -.0509, -.0451, -.041,\n            -.0346, -.0302, -.0235, -.0192, -.0150, -.0093, -.0048, -.0024,\n            -.0013, -.0008, -.0016, -.0035, -.0049, -.0066, -.0085, -.0109,\n            -.0137, -.0163};\n\n        return {{x_upper, y_upper, x_lower, y_lower}};\n\n      } else {\n\n        AssertThrow(false,\n                    dealii::ExcMessage(\"Invalid NASA SC(2) serial number\"));\n      }\n    }\n\n\n    /**\n     * Datapoints for the Onera OAT15a airfoil.\n     *\n     * This data is annoyingly hard to get, see the discussion here\n     * https://www.cfd-online.com/Forums/main/114894-oat15a-geometry.html\n     * that mentions the following thesis (Appendix page 152) as a\n     * reference https://lume.ufrgs.br/handle/10183/28925\n     */\n    std::array<std::vector<double>, 4> onera(const std::string &serial_number)\n    {\n      if (serial_number == \"OAT15a\") {\n        std::vector<double> x_upper{\n            0.,          2.95888e-05, 0.000117865, 0.000263239, 0.000464245,\n            0.000719821, 0.00103013,  0.00139605,  0.00181884,  0.00230024,\n            0.00284243,  0.00344764,  0.00411805,  0.00485595,  0.00566349,\n            0.00654241,  0.00749421,  0.0085197,   0.0096197,   0.0107945,\n            0.0120442,   0.013369,    0.0147688,   0.0162438,   0.0177939,\n            0.0194194,   0.0211207,   0.0228982,   0.0247526,   0.0266845,\n            0.028695,    0.0307852,   0.0329562,   0.0352094,   0.0375463,\n            0.0399687,   0.0424782,   0.0450769,   0.0477668,   0.05055,\n            0.0534291,   0.0564063,   0.0594842,   0.0626655,   0.0659531,\n            0.0693498,   0.0728588,   0.0764831,   0.0802261,   0.0840914,\n            0.0880824,   0.0922027,   0.0964564,   0.100847,    0.10538,\n            0.110058,    0.114885,    0.119868,    0.125009,    0.130314,\n            0.135789,    0.14139,     0.147074,    0.152839,    0.158682,\n            0.164603,    0.170599,    0.17667,     0.182814,    0.189028,\n            0.195312,    0.201665,    0.208083,    0.214567,    0.221115,\n            0.227724,    0.234394,    0.241123,    0.24791,     0.254753,\n            0.26165,     0.2686,      0.275601,    0.282653,    0.289753,\n            0.2969,      0.304093,    0.311329,    0.318609,    0.325929,\n            0.333289,    0.340686,    0.348121,    0.35559,     0.363093,\n            0.370629,    0.378194,    0.385789,    0.393412,    0.40106,\n            0.408734,    0.41643,     0.424148,    0.431886,    0.439643,\n            0.447417,    0.455207,    0.46301,     0.470827,    0.478654,\n            0.486491,    0.494337,    0.502189,    0.510046,    0.517907,\n            0.525769,    0.533632,    0.541494,    0.549354,    0.557209,\n            0.565059,    0.572902,    0.580736,    0.588559,    0.596371,\n            0.60417,     0.611953,    0.61972,     0.627469,    0.635198,\n            0.642906,    0.65059,     0.658251,    0.665886,    0.673493,\n            0.68107,     0.688617,    0.69613,     0.70361,     0.711054,\n            0.71846,     0.725827,    0.733154,    0.740438,    0.747679,\n            0.754875,    0.762025,    0.769127,    0.776181,    0.783185,\n            0.790139,    0.79704,     0.80389,     0.810685,    0.817426,\n            0.82411,     0.830738,    0.837307,    0.843817,    0.850265,\n            0.856652,    0.862974,    0.869233,    0.87538,     0.881373,\n            0.887216,    0.892913,    0.898467,    0.903883,    0.909163,\n            0.914311,    0.91933,     0.924224,    0.928996,    0.933648,\n            0.938183,    0.942606,    0.946917,    0.95112,     0.955217,\n            0.959212,    0.963107,    0.966904,    0.970605,    0.974213,\n            0.977731,    0.98116,     0.984503,    0.987762,    0.990939,\n            0.994036,    0.997056,    1.};\n\n        std::vector<double> y_upper{\n            0.,         0.000899353, 0.0018231,  0.00276894, 0.00373508,\n            0.00472011, 0.0057226,   0.00674103, 0.0077738,  0.00881906,\n            0.00987467, 0.0109383,   0.0120074,  0.0130793,  0.0141511,\n            0.01522,    0.0162832,   0.0173387,  0.0183841,  0.0194179,\n            0.0204389,  0.021446,    0.0224386,  0.0234164,  0.0243794,\n            0.0253276,  0.0262612,   0.0271805,  0.028086,   0.0289783,\n            0.0298578,  0.0307252,   0.0315811,  0.0324262,  0.0332611,\n            0.0340861,  0.0349022,   0.0357098,  0.0365095,  0.0373016,\n            0.0380867,  0.0388652,   0.0396375,  0.0404039,  0.041165,\n            0.0419211,  0.0426722,   0.0434189,  0.0441614,  0.0448995,\n            0.0456336,  0.0463636,   0.0470894,  0.047811,   0.0485286,\n            0.0492423,  0.0499518,   0.0506574,  0.0513591,  0.0520569,\n            0.0527506,  0.0534343,   0.0541023,  0.054755,   0.0553921,\n            0.0560138,  0.05662,     0.0572108,  0.0577861,  0.0583462,\n            0.0588909,  0.0594202,   0.0599341,  0.0604325,  0.0609153,\n            0.0613826,  0.0618341,   0.06227,    0.06269,    0.0630941,\n            0.0634823,  0.0638544,   0.0642103,  0.06455,    0.0648734,\n            0.0651806,  0.0654713,   0.0657454,  0.0660031,  0.0662442,\n            0.0664685,  0.066676,    0.0668664,  0.0670398,  0.067196,\n            0.0673349,  0.0674562,   0.0675598,  0.0676456,  0.0677134,\n            0.0677629,  0.067794,    0.0678065,  0.0678,     0.0677743,\n            0.0677293,  0.0676646,   0.0675798,  0.0674748,  0.0673492,\n            0.0672027,  0.0670349,   0.0668456,  0.0666344,  0.066401,\n            0.066145,   0.0658661,   0.065564,   0.0652385,  0.064889,\n            0.0645151,  0.0641169,   0.0636938,  0.0632454,  0.0627715,\n            0.0622718,  0.061746,    0.0611937,  0.0606145,  0.0600083,\n            0.0593747,  0.0587136,   0.0580244,  0.0573069,  0.0565607,\n            0.0557853,  0.0549807,   0.0541461,  0.0532814,  0.0523863,\n            0.0514606,  0.0505046,   0.0495188,  0.0485042,  0.047462,\n            0.0463943,  0.0453031,   0.0441914,  0.0430618,  0.0419174,\n            0.0407612,  0.0395961,   0.038425,   0.0372503,  0.0360742,\n            0.034899,   0.0337262,   0.0325572,  0.0313935,  0.030236,\n            0.029086,   0.0279442,   0.0268114,  0.0256966,  0.0246079,\n            0.023545,   0.0225073,   0.0214947,  0.0205065,  0.0195422,\n            0.0186011,  0.0176826,   0.0167863,  0.0159112,  0.0150567,\n            0.0142221,  0.0134066,   0.0126095,  0.0118301,  0.0110678,\n            0.0103219,  0.00959177,  0.00887695, 0.00817697, 0.00749135,\n            0.00681977, 0.0061621,   0.00551806, 0.00488739, 0.00427007,\n            0.00366612, 0.00307588,  0.0024997};\n\n        std::vector<double> x_lower{\n            0.,          3.22311e-05, 0.000136327, 0.000324365, 0.000606007,\n            0.000986654, 0.00146626,  0.00204126,  0.00270571,  0.00345312,\n            0.00427708,  0.00517234,  0.00613428,  0.00715943,  0.00824517,\n            0.00938973,  0.0105917,   0.0118504,   0.0131652,   0.0145362,\n            0.0159635,   0.0174476,   0.0189891,   0.0205889,   0.0222479,\n            0.0239675,   0.0257488,   0.0275934,   0.0295028,   0.031479,\n            0.0335237,   0.035639,    0.0378271,   0.04009,     0.0424303,\n            0.0448502,   0.0473523,   0.049939,    0.0526132,   0.0553774,\n            0.0582343,   0.0611868,   0.0642377,   0.0673899,   0.0706465,\n            0.0740105,   0.077485,    0.0810733,   0.0847788,   0.088605,\n            0.0925553,   0.0966336,   0.100844,    0.10519,     0.109675,\n            0.114305,    0.119084,    0.124016,    0.129106,    0.134358,\n            0.139779,    0.145328,    0.150961,    0.156676,    0.162473,\n            0.168349,    0.174303,    0.180333,    0.186437,    0.192615,\n            0.198863,    0.205181,    0.211568,    0.21802,     0.224537,\n            0.231117,    0.237759,    0.24446,     0.251219,    0.258035,\n            0.264905,    0.271829,    0.278804,    0.285828,    0.292901,\n            0.30002,     0.307184,    0.314391,    0.321639,    0.328927,\n            0.336253,    0.343615,    0.351011,    0.358441,    0.365902,\n            0.373392,    0.38091,     0.388455,    0.396024,    0.403616,\n            0.41123,     0.418864,    0.426517,    0.434187,    0.441871,\n            0.44957,     0.457282,    0.465005,    0.472737,    0.480477,\n            0.488225,    0.495977,    0.503733,    0.511493,    0.519252,\n            0.527012,    0.53477,     0.542525,    0.550276,    0.55802,\n            0.565759,    0.573488,    0.581208,    0.588918,    0.596615,\n            0.604298,    0.611967,    0.61962,     0.627257,    0.634874,\n            0.642473,    0.65005,     0.657605,    0.665137,    0.672645,\n            0.680128,    0.687584,    0.695012,    0.702411,    0.70978,\n            0.717118,    0.724424,    0.731697,    0.738935,    0.746137,\n            0.753303,    0.76043,     0.767518,    0.774565,    0.78157,\n            0.788531,    0.795447,    0.802316,    0.809136,    0.815905,\n            0.822623,    0.829286,    0.835893,    0.842441,    0.84893,\n            0.855357,    0.86172,     0.868018,    0.874204,    0.880239,\n            0.886123,    0.891863,    0.89746,     0.902919,    0.908242,\n            0.913433,    0.918495,    0.923431,    0.928244,    0.932938,\n            0.937515,    0.941978,    0.94633,     0.950574,    0.954712,\n            0.958747,    0.962681,    0.966518,    0.970259,    0.973907,\n            0.977464,    0.980932,    0.984314,    0.987612,    0.990827,\n            0.993963,    0.997019,    1.};\n\n        std::vector<double> y_lower{\n            0.,           -0.000899234, -0.00182108,  -0.00275889,  -0.00370397,\n            -0.00464681,  -0.00557909,  -0.00649452,  -0.0073895,   -0.00826265,\n            -0.00911444,  -0.00994611,  -0.0107598,   -0.0115578,   -0.0123422,\n            -0.0131152,   -0.0138788,   -0.0146348,   -0.0153851,   -0.0161313,\n            -0.0168749,   -0.0176173,   -0.0183597,   -0.0191032,   -0.0198489,\n            -0.0205974,   -0.0213498,   -0.0221064,   -0.0228678,   -0.0236341,\n            -0.0244053,   -0.0251817,   -0.0259627,   -0.0267482,   -0.0275378,\n            -0.0283309,   -0.029127,    -0.0299255,   -0.0307258,   -0.0315273,\n            -0.0323295,   -0.0331322,   -0.0339346,   -0.0347366,   -0.035538,\n            -0.0363383,   -0.0371378,   -0.037936,    -0.0387331,   -0.0395288,\n            -0.0403228,   -0.0411152,   -0.0419054,   -0.0426933,   -0.0434778,\n            -0.0442587,   -0.0450347,   -0.045805,    -0.0465681,   -0.0473225,\n            -0.0480666,   -0.0487929,   -0.0494941,   -0.0501695,   -0.0508179,\n            -0.0514387,   -0.052031,    -0.0525942,   -0.0531277,   -0.0536309,\n            -0.0541035,   -0.054545,    -0.0549547,   -0.0553323,   -0.0556773,\n            -0.0559891,   -0.056267,    -0.0565107,   -0.0567191,   -0.0568918,\n            -0.0570281,   -0.0571272,   -0.0571886,   -0.0572116,   -0.0571958,\n            -0.0571405,   -0.0570453,   -0.0569098,   -0.0567339,   -0.0565171,\n            -0.0562594,   -0.0559608,   -0.055621,    -0.0552405,   -0.0548193,\n            -0.0543577,   -0.0538564,   -0.0533157,   -0.0527365,   -0.0521197,\n            -0.0514661,   -0.0507767,   -0.0500525,   -0.0492947,   -0.0485041,\n            -0.0476819,   -0.0468294,   -0.0459474,   -0.0450371,   -0.0440995,\n            -0.0431356,   -0.0421465,   -0.0411333,   -0.0400971,   -0.039039,\n            -0.0379601,   -0.0368617,   -0.0357451,   -0.0346114,   -0.0334621,\n            -0.0322983,   -0.0311217,   -0.0299336,   -0.0287354,   -0.0275285,\n            -0.0263145,   -0.025095,    -0.0238717,   -0.0226459,   -0.0214194,\n            -0.020194,    -0.0189714,   -0.0177534,   -0.016542,    -0.015339,\n            -0.0141466,   -0.0129671,   -0.0118026,   -0.0106558,   -0.00952898,\n            -0.00842491,  -0.00734634,  -0.00629613,  -0.00527714,  -0.0042922,\n            -0.00334424,  -0.00243619,  -0.00157086,  -0.000750868, 2.13187e-05,\n            0.000743489,  0.00141373,   0.00203031,   0.00259174,   0.00309711,\n            0.00354599,   0.00393819,   0.00427381,   0.00455352,   0.00477839,\n            0.00494994,   0.00506989,   0.00514012,   0.0051629,    0.00514197,\n            0.0050815,    0.0049854,    0.00485738,   0.0047009,    0.00451919,\n            0.0043152,    0.00409157,   0.00385071,   0.00359475,   0.00332561,\n            0.00304503,   0.00275452,   0.00245546,   0.00214904,   0.00183633,\n            0.00151827,   0.00119574,   0.00086945,   0.00054002,   0.000208013,\n            -0.000126036, -0.000461602, -0.000798383, -0.0011363,   -0.00147529,\n            -0.00181444,  -0.00215832,  -0.0024967};\n\n        return {{x_upper, y_upper, x_lower, y_lower}};\n\n      } else {\n\n        AssertThrow(false, dealii::ExcMessage(\"Invalid ONERA serial number\"));\n      }\n    }\n\n    /**\n     * (nlr1t-il) NLR-1T AIRFOIL\n     *\n     * Bell/NASA/NLR NLR-1T rotorcraft airfoil\n     * Max thickness 8.7% at 38.3% chord.\n     * Max camber 1.3% at 22.3% chord\n     *\n     * Input by Taylor Boylan\n     */\n    std::array<std::vector<double>, 4> bell(const std::string &serial_number)\n    {\n      if (serial_number == \"NLR-1T\") {\n        std::vector<double> x_upper{\n            .0,     .00259, .00974, .02185, .03796, .05675, .07753,\n            .09845, .12341, .15412, .18767, .22313, .26054, .29979,\n            .34064, .38269, .42528, .46849, .51162, .55383, .59596,\n            .63728, .67732, .71079, .73905, .76946, .80263, .84055,\n            .87846, .90845, .93589, .96199, 1.};\n\n        std::vector<double> y_upper{\n            .0,     .00704, .01524, .02296, .02972, .03588, .04098,\n            .04469, .04741, .04986, .05188, .05345, .05459, .05531,\n            .05565, .0556,  .05518, .05438, .05323, .05175, .04992,\n            .04774, .04524, .04291, .04017, .03644, .0314,  .02533,\n            .01901, .01421, .0102,  .00651, .00104};\n\n        std::vector<double> x_lower{\n            .0,     .00259, .00974, .02185, .03796, .05675, .07753,\n            .09845, .12341, .15412, .18767, .22313, .26054, .29979,\n            .34064, .38269, .42528, .46849, .51162, .55383, .59596,\n            .63728, .67732, .71079, .73905, .76946, .80263, .84055,\n            .87846, .90845, .93589, .96199, 1.};\n        std::vector<double> y_lower{\n            .0,      -.00512, -.00867, -.0118,  -.01465, -.01713, -.01929,\n            -.02112, -.02299, -.02494, -.02671, -.02821, -.02944, -.0304,\n            -.03104, -.03142, -.0315,  -.03132, -.0308,  -.02992, -.02867,\n            -.02734, -.0258,  -.02432, -.02305, -.02164, -.01996, -.01794,\n            -.01571, -.01364, -.01087, -.00711, -.00104};\n\n        return {{x_upper, y_upper, x_lower, y_lower}};\n\n      } else {\n\n        AssertThrow(false, dealii::ExcMessage(\"Invalid BELL serial number\"));\n      }\n    }\n\n\n    /**\n     * @todo Documentation\n     */\n    std::array<std::function<double(const double)>, 3>\n    create_psi(const std::vector<double> &x_upper [[maybe_unused]],\n               const std::vector<double> &y_upper [[maybe_unused]],\n               const std::vector<double> &x_lower [[maybe_unused]],\n               const std::vector<double> &y_lower [[maybe_unused]],\n               const double x_center [[maybe_unused]],\n               const double y_center [[maybe_unused]],\n               const double scaling [[maybe_unused]] = 1.)\n    {\n      Assert(x_upper.size() >= 2, dealii::ExcInternalError());\n      Assert(x_upper.front() == 0. && x_upper.back() == 1.,\n             dealii::ExcInternalError());\n      Assert(std::is_sorted(x_upper.begin(), x_upper.end()),\n             dealii::ExcInternalError());\n\n      Assert(x_lower.size() >= 2, dealii::ExcInternalError());\n      Assert(x_lower.front() == 0. && x_lower.back() == 1.,\n             dealii::ExcInternalError());\n      Assert(std::is_sorted(x_lower.begin(), x_lower.end()),\n             dealii::ExcInternalError());\n\n      Assert(y_upper.size() == x_upper.size(), dealii::ExcInternalError());\n      Assert(y_upper.front() == 0., dealii::ExcInternalError());\n\n      Assert(y_lower.size() == x_lower.size(), dealii::ExcInternalError());\n      Assert(y_lower.front() == 0., dealii::ExcInternalError());\n\n      Assert(y_lower.back() < y_upper.back(), dealii::ExcInternalError());\n\n      Assert(0. < x_center && x_center < 1., dealii::ExcInternalError());\n\n#ifdef DEAL_II_WITH_GSL\n      CubicSpline upper_airfoil(x_upper, y_upper);\n      auto psi_upper =\n          [upper_airfoil, x_center, y_center, scaling](const double x_hat) {\n            /* Past the trailing edge return the the final upper y position: */\n            const double x = x_hat / scaling;\n            if (x > 1. - x_center)\n              return scaling * (upper_airfoil.eval(1.0) - y_center);\n            return scaling * (upper_airfoil.eval(x + x_center) - y_center);\n          };\n\n      CubicSpline lower_airfoil(x_lower, y_lower);\n\n      auto psi_lower =\n          [lower_airfoil, x_center, y_center, scaling](const double x_hat) {\n            /* Past the trailing edge return the the final lower y position: */\n            const double x = x_hat / scaling;\n            if (x > 1. - x_center)\n              return scaling * (lower_airfoil.eval(1.0) - y_center);\n            return scaling * (lower_airfoil.eval(x + x_center) - y_center);\n          };\n\n      /*\n       * Create a combined point set for psi_front:\n       */\n\n      std::vector<double> x_combined;\n      std::vector<double> y_combined;\n\n      for (std::size_t i = 0; i < x_upper.size(); ++i) {\n        if (x_upper[i] >= x_center)\n          break;\n        x_combined.push_back(x_upper[i]);\n        y_combined.push_back(y_upper[i]);\n      }\n\n      /*\n       * We are about to create a spline interpolation in polar coordinates\n       * for the front part. In order to blend this interpolation with the\n       * two splines for the upper and lower part that we have just created\n       * we have to add some additional sample points around the\n       * coordinates were we glue together\n       */\n      for (double x : {x_center, x_center + 0.01, x_center + 0.02}) {\n        x_combined.push_back(x);\n        y_combined.push_back(upper_airfoil.eval(x));\n      }\n\n      std::reverse(x_combined.begin(), x_combined.end());\n      std::reverse(y_combined.begin(), y_combined.end());\n      x_combined.pop_back();\n      y_combined.pop_back();\n\n      for (std::size_t i = 0; i < x_lower.size(); ++i) {\n        if (x_lower[i] >= x_center)\n          break;\n        x_combined.push_back(x_lower[i]);\n        y_combined.push_back(y_lower[i]);\n      }\n\n      for (double x : {x_center, x_center + 0.01, x_center + 0.02}) {\n        x_combined.push_back(x);\n        y_combined.push_back(lower_airfoil.eval(x));\n      }\n\n      /* Translate into polar coordinates: */\n\n      for (unsigned int i = 0; i < y_combined.size(); ++i) {\n        const auto x = x_combined[i] - x_center;\n        const auto y = y_combined[i] - y_center;\n\n        const auto rho = std::sqrt(x * x + y * y);\n        auto phi = std::atan2(y, x);\n        if (phi < 0)\n          phi += 2 * dealii::numbers::PI;\n\n        x_combined[i] = phi;\n        y_combined[i] = rho;\n      }\n\n      /* Ensure that x_combined is monotonically increasing: */\n      if (x_combined.back() == 0.)\n        x_combined.back() = 2. * dealii::numbers::PI;\n      Assert(std::is_sorted(x_combined.begin(), x_combined.end()),\n             dealii::ExcInternalError());\n\n      CubicSpline front_airfoil(x_combined, y_combined);\n      auto psi_front = [front_airfoil, x_center, scaling](const double phi) {\n        /* By convention we return the \"back length\" for phi == 0.: */\n        if (phi == 0.)\n          return scaling * (1. - x_center);\n\n        return scaling * front_airfoil.eval(phi);\n      };\n\n      return {{psi_front, psi_upper, psi_lower}};\n#else\n      AssertThrow(\n          false,\n          dealii::ExcNotImplemented(\"Airfoil grid needs deal.II with GSL\"));\n      return {};\n#endif\n    }\n\n\n  } // namespace\n\n\n  namespace Geometries\n  {\n    /**\n     * A generic 2D Airfoil\n     *\n     * This class implements a generic 2D airfoil. Various runtime\n     * parameters select the airfoil type (such as NACA 4 digit, some ONERA\n     * airfoils, etc) and meshing behavior. The mesh construction is\n     * divided into various steps:\n     *\n     * 1/ Parametrization:\n     *\n     * Depending on various runtime parameters a parameterization \\f$ y =\n     * \\psi_{\\text{up./lo.}}(x)\\f$ on the upper and lower trailing edge as\n     * well as a parameterization in polar coordinates\n     * \\f$ r = \\psi_{\\text{fr.}}(\\phi) \\f$ for the nose part is constructed.\n     * This is done by taking a sample of points on the upper and lower\n     * part of the airfoil and computing intermediate points with a cubic\n     * spline interpolation. Relevant runtime parameters are `airfoil type`\n     * to specify the type and serial number, `psi samples` to control the\n     * number of samples taken (if admissible), and `psi center` to control\n     * the center point for constructing the parametrizations\n     * \\f$\\psi_{\\text{x}}\\f$. The samples are assumed to be normalized so\n     * that the front is located at \\f$(0,0)\\f$ and the trailing (or blunt\n     * edge) at \\f$(1,0)\\f$, or \\f$(1,\\ast)\\f$, respectively. The\n     * coordinate system is then shifted by the `psi center` point so that\n     * the parameterizations \\f$y = \\hat\\psi_{\\text{up./lo.}}(x)\\f$ expect\n     * input in the range \\f$\\hat x\\in[0,1-x_{\\text{psi center}}]\\f$. In the\n     * same spirit \\f$\\hat\\psi_{\\text{fr.}}(\\phi)\\f$ expects input in the\n     * range \\f$\\pi/2\\le\\phi\\le3\\pi/2\\f$ and will return\n     * \\f$\\psi_{\\text{fr.}}(\\pi) = -x_{\\text{psi center}}\\f$. Also a final\n     * step the normalized parametrizations \\f$\\hat\\psi\\f$ are rescaled\n     * with the runtime parameter `airfoil length` so that the resulting\n     * airfoil has overall length `airfoil length` instead of 1.\n     *\n     * 2/ Meshing:\n     *\n     * TODO\n     *\n     * @ingroup Mesh\n     */\n    template <int dim>\n    class Airfoil : public Geometry<dim>\n    {\n    public:\n      Airfoil(const std::string &subsection)\n          : Geometry<dim>(\"airfoil\", subsection)\n      {\n        /* Parameters affecting parameterization: */\n\n        airfoil_type_ = \"NASA SC(2) 0714\";\n        this->add_parameter(\n            \"airfoil type\", airfoil_type_, \"airfoil type and serial number\");\n\n        airfoil_length_ = 2.;\n        this->add_parameter(\"airfoil length\",\n                            airfoil_length_,\n                            \"length of airfoil (leading to trailing edge)\");\n\n        psi_samples_ = 100;\n        this->add_parameter(\"psi samples\",\n                            psi_samples_,\n                            \"number of samples used for generating spline psi\");\n\n        psi_center_[0] = 0.05;\n        this->add_parameter(\"psi center\",\n                            psi_center_,\n                            \"center position of airfoil for sampling psi\");\n\n        psi_ratio_ = 0.30;\n        this->add_parameter(\n            \"psi ratio\",\n            psi_ratio_,\n            \"Scaling parameter for averages in curved nose region, can be \"\n            \"adjusted by hand to equliabrate the size of faces at the nose \"\n            \"part of the airfoil\");\n\n\n        airfoil_center_[0] = -.5;\n        this->add_parameter(\"airfoil center\",\n                            airfoil_center_,\n                            \"position of airfoil center in the mesh\");\n\n        /* Parameters affecting mesh generation: */\n\n        grading_ = 5.5;\n        this->add_parameter(\n            \"grading exponent\", grading_, \"graded mesh: exponent\");\n\n        grading_epsilon_ = 0.02;\n        this->add_parameter(\"grading epsilon\",\n                            grading_epsilon_,\n                            \"graded mesh: regularization parameter\");\n\n        grading_epsilon_trailing_ = 0.01;\n        this->add_parameter(\n            \"grading epsilon trailing\",\n            grading_epsilon_trailing_,\n            \"graded mesh: regularization parameter for trailing cells\");\n\n        height_ = 6.;\n        this->add_parameter(\n            \"height\", height_, \"height of computational domain\");\n\n        width_ = 1.;\n        this->add_parameter(\"width\", width_, \"width of computational domain\");\n\n        n_anisotropic_refinements_airfoil_ = 1;\n        this->add_parameter(\n            \"anisotropic pre refinement airfoil\",\n            n_anisotropic_refinements_airfoil_,\n            \"number of anisotropic pre refinement steps for the airfoil\");\n\n        n_anisotropic_refinements_trailing_ = 3;\n        this->add_parameter(\"anisotropic pre refinement trailing\",\n                            n_anisotropic_refinements_trailing_,\n                            \"number of anisotropic pre refinement steps for \"\n                            \"the blunt trailing edge cell\");\n\n        subdivisions_z_ = 2;\n        this->add_parameter(\"subdivisions z\",\n                            subdivisions_z_,\n                            \"number of subdivisions in z direction\");\n      }\n\n      void create_coarse_triangulation(\n          dealii::Triangulation<dim> &triangulation) const final\n      {\n        /*\n         * Step 1: Create parametrization:\n         *\n         * Runtime parameters: airfoil_type_, airfoil_length_, psi_center_,\n         * psi_samples_\n         */\n\n        const auto [x_upper, y_upper, x_lower, y_lower] = [&]() {\n          if (airfoil_type_.rfind(\"NACA \", 0) == 0) {\n            return naca_4digit_points(airfoil_type_.substr(5), psi_samples_);\n          } else if (airfoil_type_.rfind(\"NASA SC(2) \", 0) == 0) {\n            return nasa_sc2(airfoil_type_.substr(11));\n          } else if (airfoil_type_.rfind(\"ONERA \", 0) == 0) {\n            return onera(airfoil_type_.substr(6));\n          } else if (airfoil_type_.rfind(\"BELL \", 0) == 0) {\n            return bell(airfoil_type_.substr(5));\n          }\n          AssertThrow(false, ExcMessage(\"Unknown airfoil type\"));\n        }();\n\n        const auto [psi_front, psi_upper, psi_lower] =\n            create_psi(x_upper,\n                       y_upper,\n                       x_lower,\n                       y_lower,\n                       psi_center_[0],\n                       psi_center_[1],\n                       airfoil_length_);\n\n        /*\n         * Step 2: Create coarse mesh.\n         *\n         * Runtime parameters: airfoil_center_, height_,\n         */\n\n        /* The radius of the radial front part of the mesh: */\n        const auto outer_radius = 0.5 * height_;\n\n        /* by convention, psi_front(0.) returns the \"back length\" */\n        const auto back_length = psi_front(0.);\n\n        /* sharp trailing edge? */\n        const bool sharp_trailing_edge =\n            std::abs(psi_upper(back_length) - psi_lower(back_length)) < 1.0e-10;\n        AssertThrow(\n            sharp_trailing_edge ||\n                std::abs(psi_upper(back_length) - psi_lower(back_length)) >\n                    0.001 * back_length,\n            dealii::ExcMessage(\"Blunt trailing edge has a width of less than \"\n                               \"0.1% of the trailing airfoil length.\"));\n\n        /* Front part: */\n        dealii::Triangulation<2> tria_front;\n\n        {\n          std::vector<dealii::Point<2>> vertices{\n              {-outer_radius, 0.0},                                       // 0\n              {airfoil_center_[0] - psi_front(M_PI), airfoil_center_[1]}, // 1\n              {-0.5 * outer_radius, -std::sqrt(3.) / 2. * outer_radius},  // 2\n              {0.5 * outer_radius, -std::sqrt(3.) / 2. * outer_radius},   // 3\n              {0., airfoil_center_[1] + psi_lower(-airfoil_center_[0])},  // 4\n              {airfoil_center_[0] + back_length,                          //\n               airfoil_center_[1] + psi_lower(back_length)},              // 5\n              {0., airfoil_center_[1] + psi_upper(-airfoil_center_[0])},  // 6\n              {-0.5 * outer_radius, std::sqrt(3.) / 2. * outer_radius},   // 7\n              {0.5 * outer_radius, std::sqrt(3.) / 2. * outer_radius},    // 8\n          };\n\n          std::vector<dealii::CellData<2>> cells(4);\n          assign(cells[0].vertices, {2, 3, 4, 5});\n          assign(cells[1].vertices, {0, 2, 1, 4});\n          assign(cells[2].vertices, {7, 0, 6, 1});\n          if (sharp_trailing_edge) {\n            assign(cells[3].vertices, {8, 7, 5, 6});\n          } else {\n            vertices.push_back({airfoil_center_[0] + back_length,\n                                airfoil_center_[1] + psi_upper(back_length)});\n            assign(cells[3].vertices, {8, 7, 9, 6});\n          }\n\n          tria_front.create_triangulation(\n              vertices, cells, dealii::SubCellData());\n        }\n\n        /* Back part: */\n        dealii::Triangulation<2> tria_back;\n\n        if (sharp_trailing_edge) {\n          /* Back part for sharp trailing edge: */\n\n          const std::vector<dealii::Point<2>> vertices{\n              {0.5 * outer_radius, -std::sqrt(3.) / 2. * outer_radius}, // 0\n              {airfoil_center_[0] + back_length,\n               airfoil_center_[1] + psi_lower(back_length)},           // 1\n              {0.5 * outer_radius, std::sqrt(3.) / 2. * outer_radius}, // 2\n              {outer_radius, -0.5 * outer_radius},                     // 3\n              {outer_radius, 0.0},                                     // 4\n              {outer_radius, 0.5 * outer_radius},                      // 5\n          };\n\n          std::vector<dealii::CellData<2>> cells(2);\n          assign(cells[0].vertices, {0, 3, 1, 4});\n          assign(cells[1].vertices, {1, 4, 2, 5});\n\n          tria_back.create_triangulation(\n              vertices, cells, dealii::SubCellData());\n\n        } else {\n          /* Back part for blunt trailing edge: */\n\n          /* Good width for the anisotropically refined center trailing cell: */\n          double trailing_height =\n              0.5 / (0.5 + std::pow(2., n_anisotropic_refinements_airfoil_)) *\n              0.5 * outer_radius;\n\n          const std::vector<dealii::Point<2>> vertices{\n              {0.5 * outer_radius, -std::sqrt(3.) / 2. * outer_radius}, // 0\n              {airfoil_center_[0] + back_length,\n               airfoil_center_[1] + psi_lower(back_length)}, // 1\n              {airfoil_center_[0] + back_length,\n               airfoil_center_[1] + psi_upper(back_length)},           // 2\n              {0.5 * outer_radius, std::sqrt(3.) / 2. * outer_radius}, // 3\n              {outer_radius, -0.5 * outer_radius},                     // 4\n              {outer_radius, -trailing_height},                        // 5\n              {outer_radius, trailing_height},                         // 6\n              {outer_radius, 0.5 * outer_radius},                      // 7\n          };\n\n          std::vector<dealii::CellData<2>> cells(3);\n          assign(cells[0].vertices, {0, 4, 1, 5});\n          assign(cells[1].vertices, {1, 5, 2, 6});\n          assign(cells[2].vertices, {2, 6, 3, 7});\n\n          tria_back.create_triangulation(\n              vertices, cells, dealii::SubCellData());\n        }\n\n        dealii::Triangulation<2> coarse_triangulation;\n        GridGenerator::merge_triangulations(\n            {&tria_front, &tria_back}, coarse_triangulation, 1.e-12, true);\n\n        /*\n         * Step 3: Set manifold IDs and attach manifolds to preliminary\n         * coarse triangulation:\n         *\n         * Curvature for boundaries:\n         *   1 -> upper airfoil (inner boundary)\n         *   2 -> lower airfoil (inner boundary)\n         *   3 -> spherical manifold (outer boundary)\n         *\n         * Transfinite interpolation with grading on coarse cells:\n         *\n         *  10 -> bottom center cell\n         *  11 -> bottom front cell\n         *  12 -> top front cell\n         *  13 -> top center cell\n         *  14 -> bottom trailing cell\n         *  15 -> top trailing cell (sharp), center trailing cell (blunt)\n         *  16 -> top trailing cell (blunt)\n         */\n\n        /*\n         * Colorize boundary faces and add manifolds for curvature\n         * information on boundaries:\n         */\n\n        for (auto cell : coarse_triangulation.active_cell_iterators()) {\n          for (auto f : cell->face_indices()) {\n            const auto face = cell->face(f);\n            if (!face->at_boundary())\n              continue;\n\n            bool airfoil = true;\n            bool spherical_boundary = true;\n            for (const auto v : dealii::GeometryInfo<1>::vertex_indices())\n              if (std::abs((face->vertex(v)).norm() - outer_radius) < 1.0e-10)\n                airfoil = false;\n              else\n                spherical_boundary = false;\n\n            if (spherical_boundary) {\n              face->set_manifold_id(3);\n            } else if (airfoil) {\n              if (face->center()[0] <\n                  airfoil_center_[0] + back_length - 1.e-6) {\n                if (face->center()[1] >= airfoil_center_[1]) {\n                  face->set_manifold_id(1);\n                } else {\n                  face->set_manifold_id(2);\n                }\n              }\n            }\n          } /* f */\n        }   /* cell */\n\n        Manifolds::AirfoilManifold airfoil_manifold_upper{\n            airfoil_center_, psi_front, psi_upper, psi_lower, true, psi_ratio_};\n        coarse_triangulation.set_manifold(1, airfoil_manifold_upper);\n\n        Manifolds::AirfoilManifold airfoil_manifold_lower{airfoil_center_,\n                                                          psi_front,\n                                                          psi_upper,\n                                                          psi_lower,\n                                                          false,\n                                                          psi_ratio_};\n        coarse_triangulation.set_manifold(2, airfoil_manifold_lower);\n\n        dealii::SphericalManifold<2> spherical_manifold;\n        coarse_triangulation.set_manifold(3, spherical_manifold);\n\n        /*\n         * Create transfinite interpolation manifolds for the interior of\n         * the 2D coarse cells:\n         */\n\n        Assert(!sharp_trailing_edge || (coarse_triangulation.n_cells() == 6),\n               dealii::ExcInternalError());\n        Assert(sharp_trailing_edge || (coarse_triangulation.n_cells() == 7),\n               dealii::ExcInternalError());\n\n        std::vector<std::unique_ptr<dealii::Manifold<2, 2>>> manifolds;\n        manifolds.resize(sharp_trailing_edge ? 6 : 7);\n\n        /* FIXME: Remove workaround - mark cells as off limit: */\n        // WORKAROUND\n        const auto first_cell = coarse_triangulation.begin_active();\n        std::next(first_cell, 4)->set_material_id(42);\n        std::next(first_cell, sharp_trailing_edge ? 5 : 6)->set_material_id(42);\n        // end WORKAROUND\n\n        for (auto i : {0, 1, 2, 3, 5}) {\n          const auto index = 10 + i;\n\n          dealii::Point<2> center;\n          dealii::Tensor<1, 2> direction;\n          if (i < 4) {\n            /* cells: bottom center, bottom front, top front, top center */\n            direction[1] = 1.;\n          } else {\n            Assert(i == 5, dealii::ExcInternalError());\n            /* cell: center trailing (blunt) */\n            center[0] = 1.;\n            direction[0] = -1.;\n          }\n\n          Manifolds::GradingManifold<2> grading{\n              center,\n              direction,\n              grading_,\n              i == 5 ? grading_epsilon_trailing_ : grading_epsilon_};\n\n          auto transfinite =\n              std::make_unique<TransfiniteInterpolationManifold<2>>();\n          transfinite->initialize(coarse_triangulation, grading);\n\n          coarse_triangulation.set_manifold(index, *transfinite);\n          manifolds[i] = std::move(transfinite);\n        }\n\n        /* Remove erroneous manifold: */\n        if (sharp_trailing_edge)\n          coarse_triangulation.reset_manifold(5);\n\n        /*\n         * Remove unneeded manifolds now. Our custom\n         * TransfiniteInterpolationManifolds did copy all necessary\n         * geometry information from the coarse grid already. The boundary\n         * manifolds are thus not needed any more.\n         */\n\n        coarse_triangulation.reset_manifold(1);\n        coarse_triangulation.reset_manifold(2);\n        coarse_triangulation.reset_manifold(3);\n\n        /* We can set the final sequence of manifold ids: */\n        for (unsigned int i = 0; i < (sharp_trailing_edge ? 6 : 7); ++i) {\n          const auto &cell = std::next(coarse_triangulation.begin_active(), i);\n          const auto index = 10 + i;\n          if (i == 4 || i == (sharp_trailing_edge ? 5 : 6)) {\n            cell->set_manifold_id(index);\n          } else {\n            cell->set_all_manifold_ids(index);\n          }\n        }\n\n        /*\n         * Attach separate transfinite interpolation manifolds (without a\n         * grading) to the top and bottom trailing cells:\n         */\n\n        /* FIXME: Remove workaround - mark cells as off limit: */\n        // WORKAROUND\n        for (auto cell : coarse_triangulation.active_cell_iterators())\n          cell->set_material_id(42);\n        // const auto first_cell = coarse_triangulation.begin_active();\n        std::next(first_cell, 4)->set_material_id(0);\n        std::next(first_cell, sharp_trailing_edge ? 5 : 6)->set_material_id(0);\n        // end WORKAROUND\n\n        for (auto i : {4, sharp_trailing_edge ? 5 : 6}) {\n          const auto index = 10 + i;\n          auto transfinite =\n              std::make_unique<ryujin::TransfiniteInterpolationManifold<2>>();\n          transfinite->initialize(coarse_triangulation);\n          coarse_triangulation.set_manifold(index, *transfinite);\n          manifolds[i] = std::move(transfinite);\n        }\n\n        /*\n         * For good measure, also set material ids. We will need those\n         * in a minute to reconstruct material ids...\n         */\n\n        for (unsigned int i = 0; i < (sharp_trailing_edge ? 6 : 7); ++i) {\n          const auto &cell = std::next(coarse_triangulation.begin_active(), i);\n          const auto index = 10 + i;\n          cell->set_material_id(index);\n        }\n\n        /*\n         * Step 4: Anisotropic pre refinement.\n         *\n         * Runtime parameters: n_anisotropic_refinements_airfoil_,\n         * n_anisotropic_refinements_trailing_\n         */\n\n        /* Additional radials in upper and lower cell on airfoil (material\n         * ids 10 and 13): */\n        for (unsigned int i = 0; i < n_anisotropic_refinements_airfoil_; ++i) {\n          for (auto cell : coarse_triangulation.active_cell_iterators()) {\n            const auto id = cell->material_id();\n            if (id == 10 || id == 13)\n              cell->set_refine_flag(dealii::RefinementCase<2>::cut_axis(0));\n          }\n\n          coarse_triangulation.execute_coarsening_and_refinement();\n        }\n\n        /* Anisotropic refinement into trailing cell (material id 15): */\n        if (!sharp_trailing_edge)\n          for (unsigned i = 0; i < n_anisotropic_refinements_trailing_; ++i) {\n            for (auto cell : coarse_triangulation.active_cell_iterators())\n              if (cell->material_id() == 15)\n                cell->set_refine_flag(dealii::RefinementCase<2>::cut_axis(0));\n              else\n                cell->set_refine_flag();\n            coarse_triangulation.execute_coarsening_and_refinement();\n          }\n\n        /*\n         * Step 5: Flatten triangulation, create distributed coarse\n         * triangulation, and reattach manifolds\n         *\n         * Runtime parameters: width_, subdivisions_z_ (for dim == 3)\n         */\n\n        if constexpr (dim == 1) {\n          AssertThrow(false, dealii::ExcNotImplemented());\n          __builtin_trap();\n\n        } else if constexpr (dim == 2) {\n          /* Flatten manifold: */\n          dealii::Triangulation<2> tria3;\n          tria3.set_mesh_smoothing(triangulation.get_mesh_smoothing());\n          GridGenerator::flatten_triangulation(coarse_triangulation, tria3);\n\n          triangulation.copy_triangulation(tria3);\n\n        } else {\n          static_assert(dim == 3);\n\n          /* Flatten manifold: */\n          dealii::Triangulation<2> tria3;\n          GridGenerator::flatten_triangulation(coarse_triangulation, tria3);\n\n          /* extrude mesh: */\n          dealii::Triangulation<3, 3> tria4;\n          tria4.set_mesh_smoothing(triangulation.get_mesh_smoothing());\n          GridGenerator::extrude_triangulation(\n              tria3, subdivisions_z_, width_, tria4);\n\n          triangulation.copy_triangulation(tria4);\n        }\n\n        /*\n         * Somewhere during flattening the triangulation, extruding and\n         * copying, all manifold ids got lost. Reconstruct manifold IDs\n         * from the material ids we set earlier:\n         */\n\n        for (auto &cell : triangulation.active_cell_iterators()) {\n          const auto id = cell->material_id();\n          cell->set_all_manifold_ids(id);\n        }\n\n        /*\n         * Reattach manifolds:\n         */\n        if constexpr (dim == 1) {\n          AssertThrow(false, dealii::ExcNotImplemented());\n          __builtin_trap();\n\n        } else if constexpr (dim == 2) {\n          unsigned int index = 10;\n          for (const auto &manifold : manifolds)\n            triangulation.set_manifold(index++, *manifold);\n\n        } else {\n          static_assert(dim == 3);\n\n          unsigned int index = 10;\n          for (const auto &manifold : manifolds)\n            triangulation.set_manifold(\n                index++, Manifolds::ExtrudedManifold<3>(*manifold));\n        }\n\n        /* Set boundary ids: */\n\n        for (auto cell : triangulation.active_cell_iterators()) {\n          for (auto f : cell->face_indices()) {\n            auto face = cell->face(f);\n\n            /* Handle boundary faces: */\n            if (!face->at_boundary())\n              continue;\n\n            bool airfoil = true;\n            bool spherical_boundary = true;\n\n            const auto &indices =\n                dealii::GeometryInfo<dim - 1>::vertex_indices();\n            for (const auto v : indices) {\n              const auto vert = face->vertex(v);\n              const auto radius_sqr = vert[0] * vert[0] + vert[1] * vert[1];\n              if (radius_sqr >= outer_radius * outer_radius - 1.0e-10 ||\n                  vert[0] > airfoil_center_[0] + 1.001 * back_length)\n                airfoil = false;\n              else\n                spherical_boundary = false;\n            }\n\n            bool periodic_face = (dim == 3);\n\n            if constexpr (dim == 3) {\n              const auto &indices =\n                  dealii::GeometryInfo<dim - 1>::vertex_indices();\n              bool not_left = false;\n              bool not_right = false;\n              for (const auto v : indices) {\n                const auto vert = face->vertex(v);\n                if (vert[2] > 1.0e-10)\n                  not_left = true;\n                if (vert[2] < width_ - 1.0e-10)\n                  not_right = true;\n                if (not_left && not_right) {\n                  periodic_face = false;\n                  break;\n                }\n              }\n            }\n\n            if (periodic_face) {\n              face->set_boundary_id(Boundary::periodic);\n            } else if (spherical_boundary) {\n              face->set_boundary_id(Boundary::dynamic);\n            } else if (airfoil) {\n              face->set_boundary_id(Boundary::no_slip);\n            } else {\n              Assert(false, dealii::ExcInternalError());\n              __builtin_trap();\n            }\n          }\n        }\n\n        /* Add periodicity: */\n\n#ifndef BUG_COLLECT_PERIODIC_FACES_INSTANTIATION\n        if constexpr (dim == 3) {\n          std::vector<dealii::GridTools::PeriodicFacePair<\n              typename dealii::Triangulation<dim>::cell_iterator>>\n              periodic_faces;\n\n          GridTools::collect_periodic_faces(triangulation,\n                                            /*b_id */ Boundary::periodic,\n                                            /*direction*/ 2,\n                                            periodic_faces);\n\n          triangulation.add_periodicity(periodic_faces);\n        }\n#endif\n      }\n\n    private:\n      dealii::Point<2> airfoil_center_;\n      double airfoil_length_;\n      std::string airfoil_type_;\n      dealii::Point<2> psi_center_;\n      double psi_ratio_;\n      unsigned int psi_samples_;\n      double height_;\n      double width_;\n      double grading_;\n      double grading_epsilon_;\n      double grading_epsilon_trailing_;\n      unsigned int n_anisotropic_refinements_airfoil_;\n      unsigned int n_anisotropic_refinements_trailing_;\n      unsigned int subdivisions_z_;\n    };\n  } /* namespace Geometries */\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/geometries/geometry_annulus.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"geometry_common_includes.h\"\n\nnamespace ryujin\n{\n  namespace GridGenerator\n  {\n    /**\n     * Create a 2D/3D partial annulus configuration with the given length,\n     * height, radii and angle coverage.\n     *\n     * We set slip boundary conditions on all boundaries.\n     *\n     * @ingroup Mesh\n     */\n    template <int dim, int spacedim, template <int, int> class Triangulation>\n    void annulus(Triangulation<dim, spacedim> &,\n                 const double /*length*/,\n                 const double /*inner_radius*/,\n                 const double /*outer_radius*/,\n                 const double /*angle*/)\n    {\n      AssertThrow(false, dealii::ExcNotImplemented());\n      __builtin_trap();\n    }\n\n\n#ifndef DOXYGEN\n    template <template <int, int> class Triangulation>\n    void annulus(Triangulation<2, 2> &triangulation,\n                 const double length,\n                 const double inner_radius,\n                 const double outer_radius,\n                 const double angle)\n    {\n      constexpr int dim = 2;\n      constexpr double eps = 1.0e-10;\n\n      using namespace dealii;\n\n      /*\n       * A lambda that conveniently sets and assigns manifold IDs and that\n       * we need a couple of times during the construction process of the\n       * triangulation:\n       */\n      const auto assign_manifolds = [&](auto &tria) {\n        for (const auto &cell : tria.cell_iterators()) {\n          /*\n           * Mark all cells that will comprise the annulus. That is, all\n           * cells whose vertices lie betwenn the two radii.\n           */\n          bool cell_on_annulus = true;\n          for (unsigned int v : cell->vertex_indices()) {\n            const auto vertex = cell->vertex(v);\n            const auto distance = vertex.norm();\n            if (!(inner_radius - eps <= distance && distance <= outer_radius)) {\n              cell_on_annulus = false;\n              break;\n            }\n          }\n          if (cell_on_annulus)\n            cell->set_all_manifold_ids(1);\n\n          /*\n           * Separately, mark all faces that touch the annulus.\n           */\n          for (const unsigned int f : cell->face_indices()) {\n            const auto face = cell->face(f);\n\n            bool face_on_annulus = true;\n            for (unsigned int v : face->vertex_indices()) {\n              const auto vertex = face->vertex(v);\n              const auto distance = vertex.norm();\n              if (!(inner_radius - eps <= distance &&\n                    distance <= outer_radius)) {\n                face_on_annulus = false;\n                break;\n              }\n            }\n            if (face_on_annulus)\n              face->set_all_manifold_ids(1);\n          }\n        }\n\n        tria.set_manifold(1, SphericalManifold<dim>());\n        dealii::TransfiniteInterpolationManifold<dim> transfinite_manifold;\n        transfinite_manifold.initialize(tria);\n        tria.set_manifold(0, transfinite_manifold);\n      };\n\n      /* Create inner ball with radius=inner_radius: */\n      dealii::Triangulation<dim> tria_inner;\n      {\n        dealii::Triangulation<dim> temp;\n        GridGenerator::hyper_ball_balanced(\n            temp, dealii::Point<dim>(), inner_radius);\n        temp.refine_global(2);\n        GridGenerator::flatten_triangulation(temp, tria_inner);\n      }\n\n      /* Create outer annulus. Note part of this will be removed. */\n      dealii::Triangulation<dim> annulus;\n      GridGenerator::hyper_shell(\n          annulus, dealii::Point<dim>(), inner_radius, outer_radius, 32);\n\n      /* Create outside shell */\n      dealii::Triangulation<dim> tria_outer;\n      {\n        dealii::Triangulation<dim> temp;\n        GridGenerator::hyper_shell(temp,\n                                   dealii::Point<dim>(),\n                                   outer_radius,\n                                   length / 2. * std::sqrt(2),\n                                   8);\n        /* Fix up vertices so that we get back a unit square: */\n        for (const auto &cell : temp.cell_iterators()) {\n          static_assert(dim == 2, \"not implemented\");\n          for (unsigned int i = 0; i < 4; ++i) {\n            auto &vertex = cell->vertex(i);\n            if (std::abs(vertex[0]) < eps && std::abs(vertex[1]) > length / 2.)\n              vertex[1] = std::copysign(length / 2., vertex[1]);\n            if (std::abs(vertex[1]) < eps && std::abs(vertex[0]) > length / 2.)\n              vertex[0] = std::copysign(length / 2., vertex[0]);\n          }\n        }\n        assign_manifolds(temp);\n        temp.refine_global(2);\n        GridGenerator::flatten_triangulation(temp, tria_outer);\n      }\n\n      /* Create triangulation to merge: */\n      dealii::Triangulation<dim, dim> coarse_triangulation;\n      coarse_triangulation.set_mesh_smoothing(\n          triangulation.get_mesh_smoothing());\n      GridGenerator::merge_triangulations(\n          {&tria_inner, &annulus, &tria_outer}, coarse_triangulation, 1.e-12);\n\n      /*\n       * Set manifold IDs:\n       */\n\n      coarse_triangulation.reset_all_manifolds();\n      coarse_triangulation.set_all_manifold_ids(0);\n\n      assign_manifolds(coarse_triangulation);\n      coarse_triangulation.refine_global(2);\n\n      /* Remove mesh cells in the annulus */\n\n      std::set<typename dealii::Triangulation<dim>::active_cell_iterator>\n          cells_to_remove;\n\n      for (const auto &cell : coarse_triangulation.active_cell_iterators()) {\n        for (auto f : cell->face_indices()) {\n          auto face = cell->face(f);\n          const auto position = face->center();\n          const auto radius = position.norm();\n          const auto inner_value = inner_radius;\n          const auto outer_value = outer_radius;\n\n          bool in_anulus =\n              radius - inner_value > 1.e-8 && outer_value - radius > 1.e-3;\n\n          bool partial_annulus =\n              std::abs(position[1]) -\n                  std::abs(position[0]) *\n                      std::tan(dealii::numbers::PI / 180. * angle) <\n              1.e-8;\n\n          if (in_anulus && partial_annulus) {\n            cells_to_remove.insert(cell);\n          }\n        }\n      }\n\n      GridGenerator::create_triangulation_with_removed_cells(\n          coarse_triangulation, cells_to_remove, coarse_triangulation);\n\n      /*\n       * Flatten triangulation and copy over to distributed triangulation:\n       */\n      dealii::Triangulation<dim> flattened_triangulation;\n      flattened_triangulation.set_mesh_smoothing(\n          triangulation.get_mesh_smoothing());\n      GridGenerator::flatten_triangulation(coarse_triangulation,\n                                           flattened_triangulation);\n      triangulation.copy_triangulation(flattened_triangulation);\n      assign_manifolds(triangulation);\n\n\n      /*\n       * Set boundary ids:\n       */\n\n      for (auto cell : triangulation.active_cell_iterators()) {\n        for (auto f : cell->face_indices()) {\n          const auto face = cell->face(f);\n\n          if (!face->at_boundary())\n            continue;\n\n          /*\n           * We want slip boundary conditions everywhere.\n           */\n          face->set_boundary_id(Boundary::slip);\n        }\n      }\n    }\n\n\n    template <template <int, int> class Triangulation>\n    void annulus(Triangulation<3, 3> & /* triangulation */,\n                 const double /* length */,\n                 const double /* inner_radius */,\n                 const double /* outer_radius */,\n                 const double /* angle */)\n    {\n      using namespace dealii;\n      AssertThrow(false, dealii::ExcNotImplemented());\n      __builtin_trap();\n    }\n#endif\n  } /* namespace GridGenerator */\n\n\n  namespace Geometries\n  {\n    /**\n     * A 2D/3D cylinder configuration constructed with\n     * GridGenerator::annulus().\n     *\n     * @ingroup Mesh\n     */\n    template <int dim>\n    class Annulus : public Geometry<dim>\n    {\n    public:\n      Annulus(const std::string &subsection)\n          : Geometry<dim>(\"annulus\", subsection)\n      {\n        length_ = 2.;\n        this->add_parameter(\n            \"length\", length_, \"length of computational domain [-L/2,L/2]^d\");\n\n        inner_radius_ = 0.6;\n        this->add_parameter(\n            \"inner radius\", inner_radius_, \"inner radius of partial annulus\");\n\n        outer_radius_ = 0.7;\n        this->add_parameter(\n            \"outer radius\", outer_radius_, \"outer radius of partial annulus\");\n\n        angle_ = 45.;\n        this->add_parameter(\"coverage angle\",\n                            angle_,\n                            \"angle coverage of partial annulus above y-axis\");\n      }\n\n      void create_coarse_triangulation(\n          dealii::Triangulation<dim> &triangulation) const final\n      {\n        GridGenerator::annulus(\n            triangulation, length_, inner_radius_, outer_radius_, angle_);\n      }\n\n    private:\n      double length_;\n      double inner_radius_;\n      double outer_radius_;\n      double angle_;\n    };\n  } /* namespace Geometries */\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/geometries/geometry_common_includes.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <compile_time_options.h>\n\n#include \"discretization.h\"\n#include \"geometry.h\"\n\n#include <deal.II/fe/fe_system.h>\n#include <deal.II/grid/grid_generator.h>\n#include <deal.II/grid/grid_tools.h>\n#include <deal.II/grid/manifold_lib.h>\n#include <deal.II/grid/tensor_product_manifold.h>\n#include <deal.II/grid/tria.h>\n\nnamespace ryujin\n{\n  /**\n   * This namespace provides a collection of functions for generating\n   * triangulations for some benchmark configurations.\n   *\n   * @ingroup Mesh\n   */\n  namespace GridGenerator\n  {\n    using namespace dealii::GridGenerator;\n  } /* namespace GridGenerator */\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/geometries/geometry_cylinder.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2022 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"geometry_common_includes.h\"\n\nnamespace ryujin\n{\n  namespace GridGenerator\n  {\n    /**\n     * Create a 2D/3D cylinder configuration with the given length and\n     * height.\n     *\n     * We set Dirichlet boundary conditions on the left boundary and\n     * do-nothing boundary conditions on the right boundary. All other\n     * boundaries (including the cylinder) have slip boundary conditions\n     * prescribed.\n     *\n     * The 3D mesh is created by extruding the 2D mesh with a width equal\n     * to the \"height\".\n     *\n     * @ingroup Mesh\n     */\n    template <int dim, int spacedim, template <int, int> class Triangulation>\n    void cylinder(Triangulation<dim, spacedim> &,\n                  const double /*length*/,\n                  const double /*height*/,\n                  const double /*cylinder_position*/,\n                  const double /*cylinder_height*/)\n    {\n      AssertThrow(false, dealii::ExcNotImplemented());\n      __builtin_trap();\n    }\n\n\n#ifndef DOXYGEN\n    template <template <int, int> class Triangulation>\n    void cylinder(Triangulation<2, 2> &triangulation,\n                  const double length,\n                  const double height,\n                  const double cylinder_position,\n                  const double cylinder_diameter)\n    {\n      constexpr int dim = 2;\n\n      using namespace dealii;\n\n      dealii::Triangulation<dim, dim> tria1, tria2, tria3, tria4, tria5, tria6,\n          tria7;\n\n      GridGenerator::hyper_cube_with_cylindrical_hole(\n          tria1, cylinder_diameter / 2., cylinder_diameter, 0.5, 1, false);\n\n      GridGenerator::subdivided_hyper_rectangle(\n          tria2,\n          {2, 1},\n          Point<2>(-cylinder_diameter, -cylinder_diameter),\n          Point<2>(cylinder_diameter, -height / 2.));\n\n      GridGenerator::subdivided_hyper_rectangle(\n          tria3,\n          {2, 1},\n          Point<2>(-cylinder_diameter, cylinder_diameter),\n          Point<2>(cylinder_diameter, height / 2.));\n\n      GridGenerator::subdivided_hyper_rectangle(\n          tria4,\n          {6, 2},\n          Point<2>(cylinder_diameter, -cylinder_diameter),\n          Point<2>(length - cylinder_position, cylinder_diameter));\n\n      GridGenerator::subdivided_hyper_rectangle(\n          tria5,\n          {6, 1},\n          Point<2>(cylinder_diameter, cylinder_diameter),\n          Point<2>(length - cylinder_position, height / 2.));\n\n      GridGenerator::subdivided_hyper_rectangle(\n          tria6,\n          {6, 1},\n          Point<2>(cylinder_diameter, -height / 2.),\n          Point<2>(length - cylinder_position, -cylinder_diameter));\n\n      tria7.set_mesh_smoothing(triangulation.get_mesh_smoothing());\n      GridGenerator::merge_triangulations(\n          {&tria1, &tria2, &tria3, &tria4, &tria5, &tria6},\n          tria7,\n          1.e-12,\n          true);\n      triangulation.copy_triangulation(tria7);\n\n      /* Restore polar manifold for disc: */\n\n      triangulation.set_manifold(0, PolarManifold<2>(Point<2>()));\n\n      /* Fix up position of left boundary: */\n\n      for (auto cell : triangulation.active_cell_iterators())\n        for (unsigned int v : cell->vertex_indices()) {\n          auto &vertex = cell->vertex(v);\n          if (vertex[0] <= -cylinder_diameter + 1.e-6)\n            vertex[0] = -cylinder_position;\n        }\n\n      /*\n       * Set boundary ids:\n       */\n\n      for (auto cell : triangulation.active_cell_iterators()) {\n        for (auto f : cell->face_indices()) {\n          const auto face = cell->face(f);\n\n          if (!face->at_boundary())\n            continue;\n\n          /*\n           * We want slip boundary conditions (i.e. indicator 1) at top and\n           * bottom of the channel, as well as on the obstacle. On the left\n           * side we set inflow conditions (indicator 2) and on the right\n           * side we set indicator 0, i.e. do nothing.\n           */\n\n          const auto center = face->center();\n\n          if (center[0] > length - cylinder_position - 1.e-6) {\n            face->set_boundary_id(Boundary::do_nothing);\n            continue;\n          }\n\n          if (center[0] < -cylinder_position + 1.e-6) {\n            face->set_boundary_id(Boundary::dirichlet);\n            continue;\n          }\n\n          // the rest:\n          face->set_boundary_id(Boundary::slip);\n        }\n      }\n    }\n\n\n    template <template <int, int> class Triangulation>\n    void cylinder(Triangulation<3, 3> &triangulation,\n                  const double length,\n                  const double height,\n                  const double cylinder_position,\n                  const double cylinder_diameter)\n    {\n      using namespace dealii;\n\n      dealii::Triangulation<2, 2> tria1;\n\n      cylinder(tria1, length, height, cylinder_position, cylinder_diameter);\n\n      dealii::Triangulation<3, 3> tria2;\n      tria2.set_mesh_smoothing(triangulation.get_mesh_smoothing());\n\n      GridGenerator::extrude_triangulation(tria1, 4, height, tria2, true);\n      GridTools::transform(\n          [height](auto point) {\n            return point - dealii::Tensor<1, 3>{{0, 0, height / 2.}};\n          },\n          tria2);\n\n      triangulation.copy_triangulation(tria2);\n\n      /*\n       * Reattach an appropriate manifold ID:\n       */\n\n      triangulation.set_manifold(\n          0, CylindricalManifold<3>(Tensor<1, 3>{{0., 0., 1.}}, Point<3>()));\n\n      /*\n       * Set boundary ids:\n       */\n\n      for (auto cell : triangulation.active_cell_iterators()) {\n        for (auto f : cell->face_indices()) {\n          const auto face = cell->face(f);\n\n          if (!face->at_boundary())\n            continue;\n\n          /*\n           * We want slip boundary conditions (i.e. indicator 1) almost\n           * everywhere except on the faces with normal in x-direction.\n           * There, on the left side we set inflow conditions (indicator 2)\n           * and on the right side we set indicator 0, i.e. do nothing.\n           */\n\n          const auto center = face->center();\n\n          if (center[0] > length - cylinder_position - 1.e-6) {\n            face->set_boundary_id(Boundary::do_nothing);\n            continue;\n          }\n\n          if (center[0] < -cylinder_position + 1.e-6) {\n            face->set_boundary_id(Boundary::dirichlet);\n            continue;\n          }\n\n          // the rest:\n          face->set_boundary_id(Boundary::slip);\n        }\n      }\n    }\n#endif\n  } /* namespace GridGenerator */\n\n\n  namespace Geometries\n  {\n    /**\n     * A 2D/3D cylinder configuration constructed with\n     * GridGenerator::cylinder().\n     *\n     * @ingroup Mesh\n     */\n    template <int dim>\n    class Cylinder : public Geometry<dim>\n    {\n    public:\n      Cylinder(const std::string &subsection)\n          : Geometry<dim>(\"cylinder\", subsection)\n      {\n        length_ = 4.;\n        this->add_parameter(\n            \"length\", length_, \"length of computational domain\");\n\n        height_ = 2.;\n        this->add_parameter(\n            \"height\", height_, \"height of computational domain\");\n\n        object_position_ = 0.6;\n        this->add_parameter(\"object position\",\n                            object_position_,\n                            \"x position of immersed cylinder center point\");\n\n        object_diameter_ = 0.5;\n        this->add_parameter(\"object diameter\",\n                            object_diameter_,\n                            \"diameter of immersed cylinder\");\n      }\n\n      void create_coarse_triangulation(\n          dealii::Triangulation<dim> &triangulation) const final\n      {\n        GridGenerator::cylinder(triangulation,\n                                length_,\n                                height_,\n                                object_position_,\n                                object_diameter_);\n      }\n\n    private:\n      double length_;\n      double height_;\n      double object_position_;\n      double object_diameter_;\n    };\n  } /* namespace Geometries */\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/geometries/geometry_disk.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// [LANL Copyright Statement]\n// Copyright (C) 2024 - 2025 by the ryujin authors\n// Copyright (C) 2024 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"geometry_common_includes.h\"\n\nnamespace ryujin\n{\n  namespace Geometries\n  {\n    /**\n     * A 2D disk / 3D ball configuration constructed with\n     * GridGenerator::hyper_ball_balanced().\n     *\n     * @ingroup Mesh\n     */\n    template <int dim>\n    class Disk : public Geometry<dim>\n    {\n    public:\n      Disk(const std::string &subsection)\n          : Geometry<dim>(\"disk\", subsection)\n      {\n        balanced_ = true;\n        this->add_parameter(\"balanced\",\n                            balanced_,\n                            \"Use GridGenerator::hyper_ball_balanced() instead \"\n                            \"of the older GridGenerator::hyper_ball()\");\n\n        radius_ = 1.2;\n        this->add_parameter(\"radius\", radius_, \"radius of disk\");\n\n        boundary_ = Boundary::dirichlet;\n        this->add_parameter(\"boundary condition\",\n                            boundary_,\n                            \"Type of boundary condition enforced on the \"\n                            \"boundary of the disk/ball\");\n      }\n\n      void create_coarse_triangulation(\n          dealii::Triangulation<dim> &triangulation) const final\n      {\n        if (balanced_) {\n          GridGenerator::hyper_ball_balanced(\n              triangulation, dealii::Point<dim>(), radius_);\n        } else {\n          GridGenerator::hyper_ball(\n              triangulation, dealii::Point<dim>(), radius_);\n        }\n\n        /*\n         * Set boundary ids:\n         */\n        for (auto cell : triangulation.active_cell_iterators()) {\n          for (auto f : cell->face_indices()) {\n            const auto face = cell->face(f);\n\n            if (!face->at_boundary())\n              continue;\n\n            face->set_boundary_id(boundary_);\n          }\n        }\n      }\n\n    private:\n      bool balanced_;\n      double radius_;\n      Boundary boundary_;\n    };\n  } /* namespace Geometries */\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/geometries/geometry_geotiff_profile.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2022 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"geometry_common_includes.h\"\n#include \"geotiff_reader.h\"\n\nnamespace ryujin\n{\n  namespace Geometries\n  {\n    /**\n     * A ChartManifold that warps the y-direction (in 2D) or z-direction\n     * (in 3D) with a given function callable. The callable lambda must\n     * take a dealii::Point<dim> as argument and return a double that is\n     * used for the shift. The computation of the shift must only depend on\n     * the x-coordinate (in 2D) or the x and y coordinates (in 3D).\n     */\n    template <int dim, typename Callable>\n    class ProfileManifold : public dealii::ChartManifold<dim>\n    {\n    public:\n      ProfileManifold(const Callable &callable)\n          : callable_(callable)\n      {\n      }\n\n      dealii::Point<dim>\n      pull_back(const dealii::Point<dim> &space_point) const final\n      {\n        auto chart_point = space_point;\n\n        if constexpr (dim >= 2) {\n          /* transform y-direction (2D) or z-direction (3D): */\n          chart_point[dim - 1] -= callable_(space_point);\n        }\n\n        return chart_point;\n      }\n\n      dealii::Point<dim>\n      push_forward(const dealii::Point<dim> &chart_point) const final\n      {\n        auto space_point = chart_point;\n\n        if constexpr (dim >= 2) {\n          /* transform y-direction (2D) or z-direction (3D): */\n          space_point[dim - 1] += callable_(space_point);\n        }\n\n        return space_point;\n      }\n\n      std::unique_ptr<dealii::Manifold<dim, dim>> clone() const final\n      {\n        return std::make_unique<ProfileManifold<dim, Callable>>(callable_);\n      }\n\n    private:\n      const Callable callable_;\n    };\n\n\n    template <int dim, typename Callable>\n    ProfileManifold<dim, Callable>\n    make_profile_manifold(const Callable &callable)\n    {\n      return {callable};\n    }\n\n\n    /**\n     * @ingroup Mesh\n     */\n    template <int dim>\n    class GeoTIFFProfile : public Geometry<dim>\n    {\n    public:\n      GeoTIFFProfile(const std::string &subsection)\n          : Geometry<dim>(\"geotiff profile\", subsection)\n          , geotiff_reader_(subsection + \"/geotiff profile\")\n      {\n        this->add_parameter(\"position bottom left\",\n                            point_left_,\n                            \"Position of bottom left corner\");\n\n        for (unsigned int d = 0; d < dim; ++d)\n          point_right_[d] = 20.0;\n        this->add_parameter(\n            \"position top right\", point_right_, \"Position of top right corner\");\n\n        subdivisions_x_ = 1;\n        subdivisions_y_ = 1;\n        subdivisions_z_ = 1;\n        boundary_back_ = Boundary::dirichlet;\n        boundary_bottom_ = Boundary::dirichlet;\n        boundary_front_ = Boundary::dirichlet;\n        boundary_left_ = Boundary::dirichlet;\n        boundary_right_ = Boundary::dirichlet;\n        boundary_top_ = Boundary::dirichlet;\n\n        this->add_parameter(\"subdivisions x\",\n                            subdivisions_x_,\n                            \"number of subdivisions in x direction\");\n        this->add_parameter(\n            \"boundary condition left\",\n            boundary_left_,\n            \"Type of boundary condition enforced on the left side of the \"\n            \"domain (faces with normal in negative x direction)\");\n        this->add_parameter(\n            \"boundary condition right\",\n            boundary_right_,\n            \"Type of boundary condition enforced on the right side of the \"\n            \"domain (faces with normal in positive x direction)\");\n\n        if constexpr (dim >= 2) {\n          this->add_parameter(\"subdivisions y\",\n                              subdivisions_y_,\n                              \"number of subdivisions in y direction\");\n          this->add_parameter(\n              \"boundary condition bottom\",\n              boundary_bottom_,\n              \"Type of boundary condition enforced on the bottom side of the \"\n              \"domain (faces with normal in negative y direction)\");\n          this->add_parameter(\n              \"boundary condition top\",\n              boundary_top_,\n              \"Type of boundary condition enforced on the top side of the \"\n              \"domain (faces with normal in positive y direction)\");\n        }\n\n        if constexpr (dim == 2) {\n          reference_y_coordinate_ = 0.;\n          this->add_parameter(\n              \"reference y coordinate\",\n              reference_y_coordinate_,\n              \"GeoTIFF: select the value for y-coordinate in 2D. That is, the \"\n              \"1D profile for the lower boundary is queried from the 2D \"\n              \"geotiff image at coordinates (x, y=constant)\");\n        }\n\n        if constexpr (dim == 3) {\n          this->add_parameter(\"subdivisions z\",\n                              subdivisions_z_,\n                              \"number of subdivisions in z direction\");\n          this->add_parameter(\n              \"boundary condition back\",\n              boundary_back_,\n              \"Type of boundary condition enforced on the back side of the \"\n              \"domain (faces with normal in negative z direction)\");\n          this->add_parameter(\n              \"boundary condition front\",\n              boundary_front_,\n              \"Type of boundary condition enforced on the front side of the \"\n              \"domain (faces with normal in positive z direction)\");\n        }\n      }\n\n\n      void create_coarse_triangulation(\n          dealii::Triangulation<dim> &triangulation) const final\n      {\n        /* create mesh: */\n\n        dealii::Triangulation<dim, dim> tria1;\n        tria1.set_mesh_smoothing(triangulation.get_mesh_smoothing());\n\n        if constexpr (dim == 1) {\n          dealii::GridGenerator::subdivided_hyper_rectangle<dim, dim>(\n              tria1, {subdivisions_x_}, point_left_, point_right_);\n        } else if constexpr (dim == 2) {\n          dealii::GridGenerator::subdivided_hyper_rectangle(\n              tria1,\n              {subdivisions_x_, subdivisions_y_},\n              point_left_,\n              point_right_);\n        } else if constexpr (dim == 3) {\n          dealii::GridGenerator::subdivided_hyper_rectangle(\n              tria1,\n              {subdivisions_x_, subdivisions_y_, subdivisions_z_},\n              point_left_,\n              point_right_);\n        }\n\n        triangulation.copy_triangulation(tria1);\n        triangulation.reset_all_manifolds();\n        /* manifold id 0 for transfinite interpolation manifold */\n        triangulation.set_all_manifold_ids(0);\n\n        /* set boundary and manifold ids: */\n\n        for (auto cell : triangulation.active_cell_iterators()) {\n          for (auto f : cell->face_indices()) {\n            auto face = cell->face(f);\n            if (!face->at_boundary())\n              continue;\n            const auto position = face->center();\n\n            if (position[0] < point_left_[0] + 1.e-8) {\n              face->set_boundary_id(boundary_left_);\n              face->set_manifold_id(dealii::numbers::flat_manifold_id);\n            }\n\n            if (position[0] > point_right_[0] - 1.e-8) {\n              face->set_boundary_id(boundary_right_);\n              face->set_manifold_id(dealii::numbers::flat_manifold_id);\n            }\n\n\n            if constexpr (dim == 2) {\n              if (position[1] < point_left_[1] + 1.e-8) {\n                face->set_boundary_id(boundary_bottom_);\n                /* manifold id 1 for ProfileManifold: */\n                face->set_manifold_id(1);\n              }\n              if (position[1] > point_right_[1] - 1.e-8) {\n                face->set_boundary_id(boundary_top_);\n                face->set_manifold_id(dealii::numbers::flat_manifold_id);\n              }\n            }\n\n            if constexpr (dim == 3) {\n              if (position[1] < point_left_[1] + 1.e-8) {\n                face->set_boundary_id(boundary_bottom_);\n                face->set_manifold_id(dealii::numbers::flat_manifold_id);\n              }\n\n              if (position[1] > point_right_[1] - 1.e-8) {\n                face->set_boundary_id(boundary_top_);\n                face->set_manifold_id(dealii::numbers::flat_manifold_id);\n              }\n\n              /*\n               * The lower boundary at z = point_left_[2] is the profile\n               * manifold ant the upper boundary boundary at z =\n               * point_right_[2] is flat:\n               */\n\n              if (position[2] < point_left_[2] + 1.e-8) {\n                face->set_boundary_id(boundary_back_);\n                /* manifold id 1 for ProfileManifold: */\n                face->set_manifold_id(1);\n              }\n\n              if (position[2] > point_right_[2] - 1.e-8) {\n                face->set_boundary_id(boundary_front_);\n                face->set_manifold_id(dealii::numbers::flat_manifold_id);\n              }\n            }\n          } /*for*/\n        }   /*for*/\n\n        const auto profile =\n            make_profile_manifold<dim>([&](dealii::Point<dim> point) {\n              /*\n               *\n               */\n              if constexpr (dim == 1) {\n                return 0.;\n              } else if constexpr (dim == 2) {\n                /*\n                 * Set the second coordinate to a constant when querying\n                 * height information in 2D.\n                 */\n                point[1] = reference_y_coordinate_;\n                return geotiff_reader_.compute_height(point);\n              } else if constexpr (dim == 3) {\n                return geotiff_reader_.compute_height(point);\n              }\n            });\n        triangulation.set_manifold(1, profile);\n\n        dealii::TransfiniteInterpolationManifold<dim> transfinite_interpolation;\n        transfinite_interpolation.initialize(triangulation);\n        triangulation.set_manifold(0, transfinite_interpolation);\n      }\n\n    private:\n      GeoTIFFReader geotiff_reader_;\n\n      dealii::Point<dim> point_left_;\n      dealii::Point<dim> point_right_;\n\n      double reference_y_coordinate_;\n\n      unsigned int subdivisions_x_;\n      unsigned int subdivisions_y_;\n      unsigned int subdivisions_z_;\n\n      Boundary boundary_back_;\n      Boundary boundary_bottom_;\n      Boundary boundary_front_;\n      Boundary boundary_left_;\n      Boundary boundary_right_;\n      Boundary boundary_top_;\n    };\n  } /* namespace Geometries */\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/geometries/geometry_library.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2022 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"geometry_airfoil.h\"\n#include \"geometry_annulus.h\"\n#include \"geometry_cylinder.h\"\n#include \"geometry_disk.h\"\n#include \"geometry_geotiff_profile.h\"\n#include \"geometry_reader.h\"\n#include \"geometry_rectangular_domain.h\"\n#include \"geometry_step.h\"\n#include \"geometry_tank.h\"\n#include \"geometry_two_tanks.h\"\n#include \"geometry_wall.h\"\n\nnamespace ryujin\n{\n  /**\n   * A namespace for a number of benchmark geometries and dealii::GridIn\n   * wrappers.\n   *\n   * @ingroup Mesh\n   */\n  namespace Geometries\n  {\n    /**\n     * Populate a given container with all initial state defined in this\n     * namespace\n     *\n     * @ingroup Mesh\n     */\n    template <int dim, typename T>\n    void populate_geometry_list(T &geometry_list, const std::string &subsection)\n    {\n      auto add = [&](auto &&object) {\n        geometry_list.emplace(std::move(object));\n      };\n\n      add(std::make_shared<Airfoil<dim>>(subsection));\n      add(std::make_shared<Annulus<dim>>(subsection));\n      add(std::make_shared<Cylinder<dim>>(subsection));\n      add(std::make_shared<Disk<dim>>(subsection));\n      add(std::make_shared<GeoTIFFProfile<dim>>(subsection));\n      add(std::make_shared<Reader<dim>>(subsection));\n      add(std::make_shared<RectangularDomain<dim>>(subsection));\n      add(std::make_shared<Step<dim>>(subsection));\n      add(std::make_shared<TwoTanks<dim>>(subsection));\n      add(std::make_shared<Wall<dim>>(subsection));\n      add(std::make_shared<WaveTank<dim>>(subsection));\n    }\n  } /* namespace Geometries */\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/geometries/geometry_reader.h",
    "content": "#pragma once\n\n#include <compile_time_options.h>\n\n#include \"geometry_common_includes.h\"\n\n#include <deal.II/grid/grid_in.h>\n\n#include <fstream>\n\nnamespace ryujin\n{\n  namespace Geometries\n  {\n    /**\n     * This class imports a triangulation from various supported mesh files\n     * via the dealii::GridIn reader. See\n     * https://www.dealii.org/current/doxygen/deal.II/classGridIn.html\n     * for more details on supported file types and extensions.\n     *\n     * @note The mesh format must support setting boundary IDs in the mesh\n     * file. Supported boundary IDs and their meaning are collected in the\n     * Boundary enum.\n     *\n     * @ingroup Mesh\n     */\n    template <int dim>\n    class Reader : public Geometry<dim>\n    {\n    public:\n      Reader(const std::string &subsection)\n          : Geometry<dim>(\"reader\", subsection)\n      {\n        filename_ = \"ryujin.msh\";\n        this->add_parameter(\"filename\",\n                            filename_,\n                            \"The mesh file to read in via dealii::GridIn. This \"\n                            \"class supports, among others, reading in Gmsh \"\n                            \"*.msh files, and the *.ucd file format.\");\n\n        use_simplices_ = false;\n        this->add_parameter(\n            \"simplex mesh\",\n            use_simplices_,\n            \"If set to true, the triangulation is assumed to use simplices \"\n            \"instead of quadrangles.\");\n      }\n\n      void create_coarse_triangulation(\n          dealii::Triangulation<dim> &triangulation) const final\n      {\n        dealii::GridIn<dim> gridin;\n        gridin.attach_triangulation(triangulation);\n        gridin.read(filename_);\n      }\n\n      typename Geometry<dim>::HP_Collection\n      populate_hp_collections(const unsigned int /*fe_degree*/,\n                              typename ryujin::Discretization<dim>::Collection\n                                  & /*collection*/) const override\n      {\n        if (use_simplices_) {\n          return Geometry<dim>::HP_Collection::standard_simplices;\n        } else {\n          return Geometry<dim>::HP_Collection::standard_quadrilaterals;\n        }\n      }\n\n    private:\n      std::string filename_;\n      bool use_simplices_;\n    };\n  } // namespace Geometries\n} // namespace ryujin\n"
  },
  {
    "path": "source/geometries/geometry_rectangular_domain.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2022 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"geometry_common_includes.h\"\n\nnamespace ryujin\n{\n  namespace Geometries\n  {\n    /**\n     * A simple rectangular domain that is used for most validation and\n     * benchmark configurations.\n     *\n     * The rectangular domain is defined by two points, the bottom left\n     * corner \\f$(x_1,y_1,z_1)\\f$ and the top right corner\n     * \\f$(x_2,y_2,z_2)\\f$.\n     *\n     * A mesh grading can be enforced by defining an optional pull back and\n     * push forward operation.\n     *\n     * By convention the rectangular domain is orient with the x-axis to\n     * from \"left to right\", the y-axis from \"bottom to top\" and the z-axis\n     * from \"the back towards the front\".\n     *\n     * The class allows to prescribe any of the supported boundary\n     * condition on any of the 2, 4, or 6 faces.\n     *\n     * @ingroup Mesh\n     */\n    template <int dim>\n    class RectangularDomain : public Geometry<dim>\n    {\n    public:\n      RectangularDomain(const std::string &name, const std::string &subsection)\n          : Geometry<dim>(name, subsection)\n      {\n        this->add_parameter(\"position bottom left\",\n                            point_left_,\n                            \"Position of bottom left corner\");\n\n        for (unsigned int d = 0; d < dim; ++d)\n          point_right_[d] = 20.0;\n        this->add_parameter(\n            \"position top right\", point_right_, \"Position of top right corner\");\n\n        grading_push_forward_ = dim == 1 ? \"x\" : (dim == 2 ? \"x;y\" : \"x;y;z;\");\n        this->add_parameter(\"grading push forward\",\n                            grading_push_forward_,\n                            \"push forward of grading manifold\");\n\n        grading_pull_back_ = grading_push_forward_;\n        this->add_parameter(\"grading pull back\",\n                            grading_pull_back_,\n                            \"pull back of grading manifold\");\n\n        subdivisions_x_ = 1;\n        subdivisions_y_ = 1;\n        subdivisions_z_ = 1;\n        boundary_back_ = Boundary::dirichlet;\n        boundary_bottom_ = Boundary::dirichlet;\n        boundary_front_ = Boundary::dirichlet;\n        boundary_left_ = Boundary::dirichlet;\n        boundary_right_ = Boundary::dirichlet;\n        boundary_top_ = Boundary::dirichlet;\n\n        this->add_parameter(\"subdivisions x\",\n                            subdivisions_x_,\n                            \"number of subdivisions in x direction\");\n        this->add_parameter(\n            \"boundary condition left\",\n            boundary_left_,\n            \"Type of boundary condition enforced on the left side of the \"\n            \"domain (faces with normal in negative x direction)\");\n        this->add_parameter(\n            \"boundary condition right\",\n            boundary_right_,\n            \"Type of boundary condition enforced on the right side of the \"\n            \"domain (faces with normal in positive x direction)\");\n\n        if constexpr (dim >= 2) {\n          this->add_parameter(\"subdivisions y\",\n                              subdivisions_y_,\n                              \"number of subdivisions in y direction\");\n          this->add_parameter(\n              \"boundary condition bottom\",\n              boundary_bottom_,\n              \"Type of boundary condition enforced on the bottom side of the \"\n              \"domain (faces with normal in negative y direction)\");\n          this->add_parameter(\n              \"boundary condition top\",\n              boundary_top_,\n              \"Type of boundary condition enforced on the top side of the \"\n              \"domain (faces with normal in positive y direction)\");\n        }\n\n        if constexpr (dim == 3) {\n          this->add_parameter(\"subdivisions z\",\n                              subdivisions_z_,\n                              \"number of subdivisions in z direction\");\n          this->add_parameter(\n              \"boundary condition back\",\n              boundary_back_,\n              \"Type of boundary condition enforced on the back side of the \"\n              \"domain (faces with normal in negative z direction)\");\n          this->add_parameter(\n              \"boundary condition front\",\n              boundary_front_,\n              \"Type of boundary condition enforced on the front side of the \"\n              \"domain (faces with normal in positive z direction)\");\n        }\n\n        use_simplices_ = false;\n        this->add_parameter(\"simplex mesh\",\n                            use_simplices_,\n                            \"If set to true, the triangulation uses simplices \"\n                            \"instead of quadrangles.\");\n      }\n\n\n      RectangularDomain(const std::string subsection)\n          : RectangularDomain(\"rectangular domain\", subsection)\n      {\n      }\n\n\n      void create_coarse_triangulation(\n          dealii::Triangulation<dim> &triangulation) const override\n      {\n        /* create mesh: */\n\n        dealii::Triangulation<dim, dim> tria1;\n        tria1.set_mesh_smoothing(triangulation.get_mesh_smoothing());\n\n        if (use_simplices_) {\n          /* Mesh with simplices: */\n\n          if constexpr (dim == 1) {\n            dealii::GridGenerator::\n                subdivided_hyper_rectangle_with_simplices<dim, dim>(\n                    tria1, {subdivisions_x_}, point_left_, point_right_);\n          } else if constexpr (dim == 2) {\n            dealii::GridGenerator::subdivided_hyper_rectangle_with_simplices(\n                tria1,\n                {subdivisions_x_, subdivisions_y_},\n                point_left_,\n                point_right_);\n          } else if constexpr (dim == 3) {\n            dealii::GridGenerator::subdivided_hyper_rectangle_with_simplices(\n                tria1,\n                {subdivisions_x_, subdivisions_y_, subdivisions_z_},\n                point_left_,\n                point_right_);\n          }\n\n        } else {\n          /* Regular mesh with quadrilaterals/hexahedra: */\n\n          if constexpr (dim == 1) {\n            dealii::GridGenerator::subdivided_hyper_rectangle<dim, dim>(\n                tria1, {subdivisions_x_}, point_left_, point_right_);\n          } else if constexpr (dim == 2) {\n            dealii::GridGenerator::subdivided_hyper_rectangle(\n                tria1,\n                {subdivisions_x_, subdivisions_y_},\n                point_left_,\n                point_right_);\n          } else if constexpr (dim == 3) {\n            dealii::GridGenerator::subdivided_hyper_rectangle(\n                tria1,\n                {subdivisions_x_, subdivisions_y_, subdivisions_z_},\n                point_left_,\n                point_right_);\n          }\n        }\n\n        triangulation.copy_triangulation(tria1);\n\n        /* create grading: */\n\n        // FIXME: We should ideally check the push forward and pull back\n        // for compatiblity.\n        if (grading_push_forward_ != grading_pull_back_) {\n          dealii::FunctionManifold<dim> grading(grading_push_forward_,\n                                                grading_pull_back_);\n          triangulation.set_all_manifold_ids(1);\n          triangulation.set_manifold(1, grading);\n        }\n\n        /* set boundary ids: */\n\n        for (auto cell : triangulation.active_cell_iterators()) {\n          for (auto f : cell->face_indices()) {\n            auto face = cell->face(f);\n            if (!face->at_boundary())\n              continue;\n            const auto position = face->center();\n\n            if (position[0] < point_left_[0] + 1.e-8)\n              face->set_boundary_id(boundary_left_);\n            if (position[0] > point_right_[0] - 1.e-8)\n              face->set_boundary_id(boundary_right_);\n\n            if constexpr (dim >= 2) {\n              if (position[1] < point_left_[1] + 1.e-8)\n                face->set_boundary_id(boundary_bottom_);\n              if (position[1] > point_right_[1] - 1.e-8)\n                face->set_boundary_id(boundary_top_);\n            }\n\n            if constexpr (dim == 3) {\n              if (position[2] < point_left_[2] + 1.e-8)\n                face->set_boundary_id(boundary_back_);\n              if (position[2] > point_right_[2] - 1.e-8)\n                face->set_boundary_id(boundary_front_);\n            }\n          } /*for*/\n        }   /*for*/\n\n        std::vector<int> directions;\n\n        if (boundary_left_ == Boundary::periodic ||\n            boundary_right_ == Boundary::periodic) {\n          AssertThrow(boundary_left_ == boundary_right_,\n                      dealii::ExcMessage(\n                          \"For prescribing periodic boundaries in x-direction, \"\n                          \"both, the left and right boundary conditions must \"\n                          \"be set to periodic\"));\n          directions.push_back(0);\n        }\n\n        if (dim >= 2 && (boundary_bottom_ == Boundary::periodic ||\n                         boundary_top_ == Boundary::periodic)) {\n          AssertThrow(boundary_bottom_ == boundary_top_,\n                      dealii::ExcMessage(\n                          \"For prescribing periodic boundaries in y-direction, \"\n                          \"both, the bottom and top boundary conditions must \"\n                          \"be set to periodic\"));\n          directions.push_back(1);\n        }\n\n        if (dim == 3 && (boundary_back_ == Boundary::periodic ||\n                         boundary_front_ == Boundary::periodic)) {\n          AssertThrow(boundary_back_ == boundary_front_,\n                      dealii::ExcMessage(\n                          \"For prescribing periodic boundaries in z-direction, \"\n                          \"both, the back and front boundary conditions must \"\n                          \"be set to periodic\"));\n          directions.push_back(2);\n        }\n\n#ifndef BUG_COLLECT_PERIODIC_FACES_INSTANTIATION\n        if (!directions.empty()) {\n          std::vector<dealii::GridTools::PeriodicFacePair<\n              typename dealii::Triangulation<dim>::cell_iterator>>\n              periodic_faces;\n\n          for (const auto direction : directions)\n            dealii::GridTools::collect_periodic_faces(\n                triangulation,\n                /*b_id */ Boundary::periodic,\n                /*direction*/ direction,\n                periodic_faces);\n\n          triangulation.add_periodicity(periodic_faces);\n        }\n#endif\n      }\n\n\n      typename Geometry<dim>::HP_Collection\n      populate_hp_collections(const unsigned int /*fe_degree*/,\n                              typename ryujin::Discretization<dim>::Collection\n                                  & /*collection*/) const override\n      {\n        if (use_simplices_) {\n          return Geometry<dim>::HP_Collection::standard_simplices;\n        } else {\n          return Geometry<dim>::HP_Collection::standard_quadrilaterals;\n        }\n      }\n\n\n    private:\n      dealii::Point<dim> point_left_;\n      dealii::Point<dim> point_right_;\n\n      unsigned int subdivisions_x_;\n      unsigned int subdivisions_y_;\n      unsigned int subdivisions_z_;\n\n      std::string grading_push_forward_;\n      std::string grading_pull_back_;\n\n      Boundary boundary_back_;\n      Boundary boundary_bottom_;\n      Boundary boundary_front_;\n      Boundary boundary_left_;\n      Boundary boundary_right_;\n      Boundary boundary_top_;\n\n      bool use_simplices_;\n    };\n  } /* namespace Geometries */\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/geometries/geometry_step.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2022 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"geometry_common_includes.h\"\n\nnamespace ryujin\n{\n  namespace GridGenerator\n  {\n    /**\n     * Create the 2D mach step triangulation.\n     *\n     * Even though this function has a template parameter @p dim, it is only\n     * implemented for dimension 2.\n     *\n     * @ingroup Mesh\n     */\n    template <int dim, template <int, int> class Triangulation>\n    void step(Triangulation<dim, dim> &,\n              const double /*length*/,\n              const double /*height*/,\n              const double /*step_position*/,\n              const double /*step_height*/)\n    {\n      AssertThrow(false, dealii::ExcNotImplemented());\n      __builtin_trap();\n    }\n\n\n#ifndef DOXYGEN\n    template <template <int, int> class Triangulation>\n    void step(Triangulation<2, 2> &triangulation,\n              const double length,\n              const double height,\n              const double step_position,\n              const double step_height)\n    {\n      using namespace dealii;\n\n      dealii::Triangulation<2, 2> tria1, tria2, tria3;\n      tria3.set_mesh_smoothing(triangulation.get_mesh_smoothing());\n\n      GridGenerator::subdivided_hyper_rectangle(\n          tria1, {15, 4}, Point<2>(0., step_height), Point<2>(length, height));\n\n      GridGenerator::subdivided_hyper_rectangle(\n          tria2,\n          {3, 1},\n          Point<2>(0., 0.),\n          Point<2>(step_position, step_height));\n\n      GridGenerator::merge_triangulations(tria1, tria2, tria3);\n      triangulation.copy_triangulation(tria3);\n\n      /*\n       * Set boundary ids:\n       */\n\n      for (auto cell : triangulation.active_cell_iterators()) {\n        for (auto f : cell->face_indices()) {\n          const auto face = cell->face(f);\n\n          if (!face->at_boundary())\n            continue;\n\n          /*\n           * We want slip boundary conditions (i.e. indicator 1) at top\n           * and bottom of the rectangle. On the left we set inflow\n           * conditions (i.e. indicator 2), and we do nothing on the right\n           * side (i.e. indicator 0).\n           */\n\n          const auto center = face->center();\n\n          if (center[0] > 0. + 1.e-6 && center[0] < length - 1.e-6)\n            face->set_boundary_id(Boundary::slip);\n\n          if (center[0] < 0. + 1.e-06)\n            face->set_boundary_id(Boundary::dirichlet);\n        }\n      }\n\n      /*\n       * Refine four times and round off corner with radius 0.0125:\n       */\n\n      triangulation.refine_global(4);\n\n      Point<2> point(step_position + 0.0125, step_height - 0.0125);\n      triangulation.set_manifold(1, SphericalManifold<2>(point));\n\n      for (auto cell : triangulation.active_cell_iterators())\n        for (unsigned int v : cell->vertex_indices()) {\n          double distance =\n              (cell->vertex(v) - Point<2>(step_position, step_height)).norm();\n          if (distance < 1.e-6) {\n            for (auto f : cell->face_indices()) {\n              const auto face = cell->face(f);\n              if (face->at_boundary())\n                face->set_manifold_id(1);\n              cell->set_manifold_id(1); // temporarily for second loop\n            }\n          }\n        }\n\n      for (auto cell : triangulation.active_cell_iterators()) {\n        if (cell->manifold_id() != 1)\n          continue;\n\n        cell->set_manifold_id(0); // reset manifold id again\n\n        for (unsigned int v : cell->vertex_indices()) {\n          auto &vertex = cell->vertex(v);\n\n          if (std::abs(vertex[0] - step_position) < 1.e-6 &&\n              vertex[1] > step_height - 1.e-6)\n            vertex[0] = step_position + 0.0125 * (1 - std::sqrt(1. / 2.));\n\n          if (std::abs(vertex[1] - step_height) < 1.e-6 &&\n              vertex[0] < step_position + 0.005)\n            vertex[1] = step_height - 0.0125 * (1 - std::sqrt(1. / 2.));\n        }\n      }\n    }\n#endif\n  } /* namespace GridGenerator */\n\n\n  namespace Geometries\n  {\n    /**\n     * A 2D Mach step configuration constructed with GridGenerator::step().\n     *\n     * @ingroup Mesh\n     */\n    template <int dim>\n    class Step : public Geometry<dim>\n    {\n    public:\n      Step(const std::string &subsection)\n          : Geometry<dim>(\"step\", subsection)\n      {\n        length_ = 3.;\n        this->add_parameter(\n            \"length\", length_, \"length of computational domain\");\n\n        height_ = 1.;\n        this->add_parameter(\n            \"height\", height_, \"height of computational domain\");\n\n        step_position_ = 0.6;\n        this->add_parameter(\n            \"step position\", step_position_, \"x position of step\");\n\n        step_height_ = 0.2;\n        this->add_parameter(\"step height\", step_height_, \"height of step\");\n      }\n\n      void create_coarse_triangulation(\n          dealii::Triangulation<dim> &triangulation) const final\n      {\n        GridGenerator::step(\n            triangulation, length_, height_, step_position_, step_height_);\n      }\n\n    private:\n      double length_;\n      double height_;\n      double step_position_;\n      double step_height_;\n    };\n  } /* namespace Geometries */\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/geometries/geometry_tank.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2024 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"geometry_common_includes.h\"\n\nnamespace ryujin\n{\n  namespace GridGenerator\n  {\n    /**\n     * Create a 2D numerical wave tank with specifications outlined in Figure 4\n     * of @cite 2018-SW-exps. The tank consists of a rectangular water reservoir\n     * with a long flume attached to the \"center\" of the reservoir. For the sake\n     * of simplicity, we assume the centerline of the flume and reservoir align\n     * at y = 0.\n     *\n     * All outer walls have \"slip\" boundary conditions except outer wall at the\n     * end of the tank which has dynamic outflow conditions.\n     *\n     * @ingroup Mesh\n     */\n\n    template <int dim, int spacedim, template <int, int> class Triangulation>\n    void wavetank(Triangulation<dim, spacedim> &,\n                  const double /*reservoir_length*/,\n                  const double /*reservoir_width*/,\n                  const double /*flume_length*/,\n                  const double /*flume_width*/)\n    {\n      AssertThrow(false, dealii::ExcNotImplemented());\n      __builtin_trap();\n    }\n\n\n#ifndef DOXYGEN\n    template <template <int, int> class Triangulation>\n    void wavetank(Triangulation<2, 2> &triangulation,\n                  const double reservoir_length,\n                  const double reservoir_width,\n                  const double flume_length,\n                  const double flume_width)\n    {\n      using namespace dealii;\n\n      dealii::Triangulation<2, 2> res1, res2, res3, flume, final;\n\n      const double tolerance = 1.e-8;\n\n      Assert(reservoir_width - flume_width > tolerance,\n             dealii::ExcInternalError());\n\n      /* We split the reservoir into three triangulations and subdivide to\n       * get close to uniform refinement */\n\n      const double diff = (reservoir_width - flume_width) / 2.;\n      unsigned int sub_x = int(std::round(reservoir_length * 100.));\n      unsigned int sub_y = int(std::round(diff * 100.));\n\n      GridGenerator::subdivided_hyper_rectangle(\n          res1,\n          {sub_x, sub_y},\n          Point<2>(-reservoir_length, -reservoir_width / 2.),\n          Point<2>(0, -flume_width / 2.));\n\n      GridGenerator::subdivided_hyper_rectangle(\n          res3,\n          {sub_x, sub_y},\n          Point<2>(-reservoir_length, flume_width / 2.),\n          Point<2>(0, reservoir_width / 2.));\n\n      sub_y = int(std::round(flume_width * 100.));\n\n      GridGenerator::subdivided_hyper_rectangle(\n          res2,\n          {sub_x, sub_y},\n          Point<2>(-reservoir_length, -flume_width / 2.),\n          Point<2>(0, flume_width / 2.));\n\n      sub_x = int(std::round(flume_length * 100.));\n\n      GridGenerator::subdivided_hyper_rectangle(\n          flume,\n          {sub_x, sub_y},\n          Point<2>(0., -flume_width / 2.),\n          Point<2>(flume_length, flume_width / 2.));\n\n      final.set_mesh_smoothing(triangulation.get_mesh_smoothing());\n      GridGenerator::merge_triangulations(\n          {&res1, &res2, &res3, &flume}, final, tolerance);\n      triangulation.copy_triangulation(final);\n\n      /*\n       * Set boundary ids:\n       */\n\n      for (auto cell : triangulation.active_cell_iterators()) {\n        for (auto f : cell->face_indices()) {\n          const auto face = cell->face(f);\n\n          if (!face->at_boundary())\n            continue;\n\n          /*\n           * We want slip everywhere except right edge of tank.\n           */\n\n          face->set_boundary_id(Boundary::slip);\n\n          const auto center = face->center();\n          if (center[0] > flume_length - tolerance)\n            face->set_boundary_id(Boundary::dynamic);\n\n        } /*f*/\n      }   /*cell*/\n    }\n#endif\n  } /* namespace GridGenerator */\n\n\n  namespace Geometries\n  {\n    /**\n     * A 2D wave tank configuration constructed with GridGenerator::wavetank().\n     *\n     * @ingroup Mesh\n     */\n    template <int dim>\n    class WaveTank : public Geometry<dim>\n    {\n    public:\n      WaveTank(const std::string &subsection)\n          : Geometry<dim>(\"wave tank\", subsection)\n      {\n        reservoir_length_ = 157. / 100.;\n        this->add_parameter(\"reservoir length\",\n                            reservoir_length_,\n                            \"length of water reservoir [meters]\");\n\n        reservoir_width_ = 81 / 100.;\n        this->add_parameter(\"reservoir width\",\n                            reservoir_width_,\n                            \"width of water reservoir [meters]\");\n\n        flume_length_ = 600.78 / 100.;\n        this->add_parameter(\n            \"flume length\", flume_length_, \"length of flume [meters]\");\n\n        flume_width_ = 24 / 100.;\n        this->add_parameter(\n            \"flume width\", flume_width_, \"width of flume [meters]\");\n      }\n\n      void create_coarse_triangulation(\n          dealii::Triangulation<dim> &triangulation) const final\n      {\n        GridGenerator::wavetank(triangulation,\n                                reservoir_length_,\n                                reservoir_width_,\n                                flume_length_,\n                                flume_width_);\n      }\n\n    private:\n      double reservoir_length_;\n      double reservoir_width_;\n      double flume_length_;\n      double flume_width_;\n    };\n  } /* namespace Geometries */\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/geometries/geometry_two_tanks.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"geometry_common_includes.h\"\n\nnamespace ryujin\n{\n  namespace GridGenerator\n  {\n    /**\n     * Create a 2D numerical tank configuration.\n     *\n     * The domain consists of two \"tanks\" attached via a \"tunnel\".\n     * For the sake of simplicity, we assume a couple of things:\n     *\n     *    1. the two tanks are the same size,\n     *    2. the centerline of tanks and tunnel align at y = 0.\n     *\n     * The walls of the tunnel have \"slip\" (or wall / reflecting) boundary\n     * conditions. The \"inner\" walls also have slip boundary conditions.\n     * For the left/right external boundaries, we assume \"dynamic\".\n     *\n     * NOTE: The default parameters are arbitrary.\n     *\n     * @ingroup Mesh\n     */\n\n    template <int dim, int spacedim, template <int, int> class Triangulation>\n    void twotanks(Triangulation<dim, spacedim> &,\n                  const double /*tank_length*/,\n                  const double /*tank_width*/,\n                  const double /*tunnel_length*/,\n                  const double /*tunnel_width*/,\n                  const unsigned int /*subdivisions_factor*/)\n    {\n      AssertThrow(false, dealii::ExcNotImplemented());\n      __builtin_trap();\n    }\n\n\n#ifndef DOXYGEN\n    template <template <int, int> class Triangulation>\n    void twotanks(Triangulation<2, 2> &triangulation,\n                  const double tank_length,\n                  const double tank_width,\n                  const double tunnel_length,\n                  const double tunnel_width,\n                  const unsigned int subdivisions_factor)\n    {\n      using namespace dealii;\n\n      dealii::Triangulation<2, 2> res1, res2, res3, tank1, tank2, tunnel, final;\n\n      const double tolerance = 1.e-8;\n\n      Assert(\n          tank_width - tunnel_width > tolerance,\n          dealii::ExcMessage(\n              \" !!! The tank width must be larger than the tunnel width !!!\"));\n\n      /* We split the tank into three triangulations and subdivide to\n       * get somewhat close to uniform refinement */\n\n      const double diff = (tank_width - tunnel_width) / 2.;\n      unsigned int sub_x =\n          static_cast<int>(std::round(tank_length * subdivisions_factor));\n      unsigned int sub_y =\n          static_cast<int>(std::round(diff * subdivisions_factor));\n\n      GridGenerator::subdivided_hyper_rectangle(\n          res1,\n          {sub_x, sub_y},\n          Point<2>(-tank_length, -tank_width / 2.),\n          Point<2>(0, -tunnel_width / 2.));\n\n      GridGenerator::subdivided_hyper_rectangle(\n          res3,\n          {sub_x, sub_y},\n          Point<2>(-tank_length, tunnel_width / 2.),\n          Point<2>(0, tank_width / 2.));\n\n      sub_y = static_cast<int>(std::round(tunnel_width * subdivisions_factor));\n\n      GridGenerator::subdivided_hyper_rectangle(\n          res2,\n          {sub_x, sub_y},\n          Point<2>(-tank_length, -tunnel_width / 2.),\n          Point<2>(0, tunnel_width / 2.));\n\n      // We create tank1 by merging the three triangulations above\n      tank1.set_mesh_smoothing(triangulation.get_mesh_smoothing());\n      GridGenerator::merge_triangulations(\n          {&res1, &res2, &res3}, tank1, tolerance);\n\n      // We now create the second tank (tank2) by copying the above and shifting\n      tank2.copy_triangulation(tank1);\n      dealii::Point<2> shift_vector(tunnel_length + tank_length, 0.);\n      dealii::GridTools::shift(shift_vector, tank2);\n\n      // We now create the tunnel\n      sub_x = static_cast<int>(std::round(tunnel_length * subdivisions_factor));\n\n      GridGenerator::subdivided_hyper_rectangle(\n          tunnel,\n          {sub_x, sub_y},\n          Point<2>(0., -tunnel_width / 2.),\n          Point<2>(tunnel_length, tunnel_width / 2.));\n\n\n      // We now merge the two tanks and the tunnel\n      final.set_mesh_smoothing(triangulation.get_mesh_smoothing());\n      GridGenerator::merge_triangulations(\n          {&tank1, &tunnel, &tank2}, final, tolerance);\n\n\n      // Finally, copy the \"final\" triangulation to \"triangulation\"\n      triangulation.copy_triangulation(final);\n\n      /*\n       * Set boundary ids:\n       */\n\n      for (auto cell : triangulation.active_cell_iterators()) {\n        for (auto f : cell->face_indices()) {\n          const auto face = cell->face(f);\n\n          if (!face->at_boundary())\n            continue;\n\n          /*\n           * We want slip everywhere except the left/right edges of tanks.\n           */\n\n          face->set_boundary_id(Boundary::slip);\n\n          const auto center = face->center();\n          if (center[0] > tank_length + tunnel_length - tolerance)\n            face->set_boundary_id(Boundary::dynamic);\n\n          if (center[0] < -tank_length + tolerance)\n            face->set_boundary_id(Boundary::dynamic);\n\n        } /*f*/\n      }   /*cell*/\n    }\n#endif\n  } /* namespace GridGenerator */\n\n\n  namespace Geometries\n  {\n    /**\n     * A 2D tank configuration constructed with GridGenerator::twotanks().\n     *\n     * @ingroup Mesh\n     */\n    template <int dim>\n    class TwoTanks : public Geometry<dim>\n    {\n    public:\n      TwoTanks(const std::string &subsection)\n          : Geometry<dim>(\"two tanks\", subsection)\n      {\n        tank_length_ = 100.;\n        this->add_parameter(\n            \"tank length\", tank_length_, \"length of tanks [units]\");\n\n        tank_width_ = 100.;\n        this->add_parameter(\"tank width\", tank_width_, \"width of tank [units]\");\n\n        tunnel_length_ = 10.;\n        this->add_parameter(\n            \"tunnel length\", tunnel_length_, \"length of tunnel [units]\");\n\n        tunnel_width_ = 50.;\n        this->add_parameter(\n            \"tunnel width\", tunnel_width_, \"width of tunnel [units]\");\n\n        // if you divide the default values by 100, make this number 100\n        subdivisions_factor_ = 1;\n        this->add_parameter(\"subdivisions factor\",\n                            subdivisions_factor_,\n                            \"A number used for introducing subdivions in both \"\n                            \"x-y direction. Useful when dealing with \"\n                            \"measurements that are less than 1. \");\n      }\n\n      void create_coarse_triangulation(\n          dealii::Triangulation<dim> &triangulation) const final\n      {\n        GridGenerator::twotanks(triangulation,\n                                tank_length_,\n                                tank_width_,\n                                tunnel_length_,\n                                tunnel_width_,\n                                subdivisions_factor_);\n      }\n\n    private:\n      double tank_length_;\n      double tank_width_;\n      double tunnel_length_;\n      double tunnel_width_;\n\n      unsigned int subdivisions_factor_;\n    };\n  } /* namespace Geometries */\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/geometries/geometry_wall.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2022 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"geometry_common_includes.h\"\n\nnamespace ryujin\n{\n  namespace GridGenerator\n  {\n    /**\n     * Create a 2D double-mach reflection wall configuration:\n     *\n     * A rectangular domain with given length and height. The boundary\n     * conditions are dirichlet conditions on the top and left of the\n     * domain and do-nothing on the right side and the front part of the\n     * bottom side from `[0, wall_position)`. Slip boundary conditions are\n     * enforced on the bottom side on `[wall_position, length)`.\n     *\n     * @ingroup Mesh\n     */\n\n    template <int dim, int spacedim, template <int, int> class Triangulation>\n    void wall(Triangulation<dim, spacedim> &,\n              const double /*length*/,\n              const double /*height*/,\n              const double /*wall_position*/)\n    {\n      AssertThrow(false, dealii::ExcNotImplemented());\n      __builtin_trap();\n    }\n\n\n#ifndef DOXYGEN\n    template <template <int, int> class Triangulation>\n    void wall(Triangulation<2, 2> &triangulation,\n              const double length,\n              const double height,\n              const double wall_position)\n    {\n      using namespace dealii;\n\n      dealii::Triangulation<2, 2> tria1, tria2, tria3;\n      tria3.set_mesh_smoothing(triangulation.get_mesh_smoothing());\n\n      GridGenerator::subdivided_hyper_rectangle(\n          tria1, {18, 6}, Point<2>(wall_position, 0), Point<2>(length, height));\n\n      GridGenerator::subdivided_hyper_rectangle(\n          tria2, {1, 6}, Point<2>(0., 0.), Point<2>(wall_position, height));\n\n      GridGenerator::merge_triangulations(tria1, tria2, tria3);\n\n      triangulation.copy_triangulation(tria3);\n\n      /*\n       * Set boundary ids:\n       */\n\n      for (auto cell : triangulation.active_cell_iterators()) {\n        for (auto f : cell->face_indices()) {\n          const auto face = cell->face(f);\n\n          if (!face->at_boundary())\n            continue;\n\n          /*\n           * We want slip boundary conditions (i.e. indicator 1) at the\n           * bottom starting at position wall_position. We do nothing on the\n           * right boundary and enforce inflow conditions elsewhere\n           */\n\n          const auto center = face->center();\n\n          if (center[0] > wall_position && center[1] < 1.e-6) {\n            face->set_boundary_id(Boundary::slip);\n\n          } else if (center[0] > length - 1.e-6) {\n\n            face->set_boundary_id(Boundary::do_nothing);\n\n          } else {\n\n            // the rest:\n            face->set_boundary_id(Boundary::dirichlet);\n          }\n        } /*f*/\n      }   /*cell*/\n    }\n#endif\n  } /* namespace GridGenerator */\n\n\n  namespace Geometries\n  {\n    /**\n     * A 2D Mach step configuration constructed with GridGenerator::step().\n     *\n     * @ingroup Mesh\n     */\n    template <int dim>\n    class Wall : public Geometry<dim>\n    {\n    public:\n      Wall(const std::string &subsection)\n          : Geometry<dim>(\"wall\", subsection)\n      {\n        length_ = 3.2;\n        this->add_parameter(\n            \"length\", length_, \"length of computational domain\");\n\n        height_ = 1.0;\n        this->add_parameter(\n            \"height\", height_, \"height of computational domain\");\n\n        wall_position_ = 1. / 6.;\n        this->add_parameter(\n            \"wall position\", wall_position_, \"x position of wall\");\n      }\n\n      void create_coarse_triangulation(\n          dealii::Triangulation<dim> &triangulation) const final\n      {\n        GridGenerator::wall(triangulation, length_, height_, wall_position_);\n      }\n\n    private:\n      double length_;\n      double height_;\n      double wall_position_;\n    };\n  } /* namespace Geometries */\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/geotiff_reader.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2024 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"convenience_macros.h\"\n#include \"lazy.h\"\n#include \"patterns_conversion.h\"\n\n#include <deal.II/base/parameter_acceptor.h>\n\n#include <numeric>\n\n#ifdef WITH_GDAL\n#include <cpl_conv.h>\n#include <gdal.h>\n#include <gdal_priv.h>\n#endif\n\nnamespace ryujin\n{\n  enum class HeightNormalization {\n    /** do not normalize the height: **/\n    none,\n    /** set minimum to zero: **/\n    minimum,\n    /** set average to zero: **/\n    average,\n    /** set maximum to zero: **/\n    maximum\n  };\n} // namespace ryujin\n\n#ifndef DOXYGEN\nDECLARE_ENUM(ryujin::HeightNormalization,\n             LIST({ryujin::HeightNormalization::none, \"none\"},\n                  {ryujin::HeightNormalization::minimum, \"minimum\"},\n                  {ryujin::HeightNormalization::average, \"average\"},\n                  {ryujin::HeightNormalization::maximum, \"maximum\"}));\n#endif\n\nnamespace ryujin\n{\n  /**\n   * A simple GeoTIFF reader based on the GDAL library. See\n   * https://gdal.org/index.html for details on GDAL and what image\n   * formats it supports.\n   *\n   * @ingroup ShallowWaterEquations\n   */\n  class GeoTIFFReader : dealii::ParameterAcceptor\n  {\n  public:\n    GeoTIFFReader(const std::string subsection)\n        : ParameterAcceptor(subsection)\n    {\n      filename_ = \"ryujin.tif\";\n      this->add_parameter(\"filename\", filename_, \"GeoTIFF: image file to load\");\n\n      transformation_ = {0., 0.01, 0., 0., 0., 0.01};\n      this->add_parameter(\n          \"transformation\",\n          transformation_,\n          \"Array \\\"t[]\\\" describing an affine transformation between image \"\n          \"space (indices i and j from bottom left) and real coordinates (x \"\n          \"and y): x = t[0] + t[1] * i + t[2] * j, and y = t[3] + t[4] * i + \"\n          \"t[5] * j. (This transformation sets the origin of the image space \"\n          \"into the bottom left corner with index i to the right and index j \"\n          \"up)\");\n\n      transformation_allow_out_of_bounds_queries_ = false;\n      this->add_parameter(\n          \"transformation allow out of bounds queries\",\n          transformation_allow_out_of_bounds_queries_,\n          \"GeoTIFF: allow out-of-bounds queries. When set to true, the reader \"\n          \"returns constant extended values for coordinates that are outside \"\n          \"of the image range.\");\n\n      transformation_use_geotiff_ = true;\n      this->add_parameter(\"transformation use geotiff\",\n                          transformation_use_geotiff_,\n                          \"GeoTIFF: read in transformation from GeoTIFF for \"\n                          \"constructing the affine transformation. If set to \"\n                          \"false the manually specified transformation \"\n                          \"parameters will be used instead.\");\n\n      transformation_use_geotiff_origin_ = false;\n      this->add_parameter(\n          \"transformation use geotiff origin\",\n          transformation_use_geotiff_origin_,\n          \"GeoTIFF: read in affine shift (i.e., position of \"\n          \"lower left corner) from GeoTIFF for constructing \"\n          \"the affine transformation. If set to false the origin specified \"\n          \"in the transformation parameter will be used instead.\");\n\n      height_normalization_ = HeightNormalization::minimum;\n      this->add_parameter(\"height normalization\",\n                          height_normalization_,\n                          \"GeoTIFF: choose base point for height normalization \"\n                          \"that is set to 0.: none, minimum, average, maximum\");\n\n      height_scaling_ = 1.0;\n      this->add_parameter(\"height scaling\",\n                          height_scaling_,\n                          \"GeoTIFF: choose base point for height normalization \"\n                          \"that is set to 0.: none, minimum, average, maximum\");\n\n      const auto set_up = [&] {\n#ifdef WITH_GDAL\n        /* Initial GDAL and reset all data: */\n        GDALAllRegister();\n        driver_name_ = \"\";\n        driver_projection_ = \"\";\n        affine_transformation_ = {0, 0, 0, 0, 0, 0};\n        inverse_affine_transformation_ = {0, 0, 0, 0, 0, 0};\n        raster_offset_ = {0, 0};\n        raster_size_ = {0, 0};\n        raster_.clear();\n#endif\n      };\n\n      set_up();\n      this->parse_parameters_call_back.connect(set_up);\n    }\n\n    /*\n     * Query height information for a given position. The method is\n     * templated in dim, so that it can be called with a @p point having an\n     * arbitrary dimension. The method, however, only only uses the first\n     * two coordinates, x and y, from @p point, translates (x, y) into\n     * image coordinates (i, j) and returns an interpolated height value.\n     *\n     * @note If dim < 2, then the y coordinate is set to zero. Similarly,\n     * if dim == 0, then the tiff raster is queried at coordinate (0, 0).\n     */\n    template <int dim>\n    DEAL_II_ALWAYS_INLINE inline double\n    compute_height(const dealii::Point<dim> &point) const\n    {\n      geotiff_guard_.ensure_initialized([&]() {\n        read_in_raster();\n        return true;\n      });\n\n      double x = 0;\n      if constexpr (dim >= 1)\n        x = point[0];\n\n      double y = 0;\n      if constexpr (dim >= 2)\n        y = point[1];\n\n      const auto &[di, dj] = apply_inverse_transformation(x, y);\n\n      /* Check that we are in bounds: */\n\n      const bool in_bounds =\n          di > -1.0 && di < static_cast<double>(raster_size_[0]) + 1.0 &&\n          dj > -1.0 && dj < static_cast<double>(raster_size_[1]) + 1.0;\n\n#ifdef DEBUG_OUTPUT\n      if (!in_bounds) {\n        std::cout << std::setprecision(16);\n        std::cout << \"Queried point out of bounds.\" << std::endl;\n        std::cout << \"Point: \" << point << std::endl;\n        std::cout << \"Transformed coordinates: (\" << di << \",\" << dj << \")\"\n                  << std::endl;\n      }\n#endif\n\n      AssertThrow(\n          transformation_allow_out_of_bounds_queries_ || in_bounds,\n          dealii::ExcMessage(\"Raster error: The requested point is outside \"\n                             \"the image boundary of the geotiff file\"));\n\n      /*\n       * Use a simple bilinear interpolation and ensure we never go below\n       * the minimum or above the maximum index.\n       */\n\n      const auto i_left = std::min(\n          std::max(static_cast<int>(std::floor(di)), 0), raster_size_[0]);\n      const auto i_right = std::min(\n          std::max(static_cast<int>(std::ceil(di)), 0), raster_size_[0]);\n      const auto j_left = std::min(\n          std::max(static_cast<int>(std::floor(dj)), 0), raster_size_[1]);\n      const auto j_right = std::min(\n          std::max(static_cast<int>(std::ceil(dj)), 0), raster_size_[1]);\n\n#ifdef DEBUG_OUTPUT\n      if (!in_bounds) {\n        std::cout << \"index bounding box: (\" << i_left << \",\" << j_left\n                  << \") and (\" << i_right << \",\" << j_right << \")\" << std::endl;\n      }\n#endif\n\n      const double i_ratio = std::fmod(di, 1.);\n      const double j_ratio = std::fmod(dj, 1.);\n\n      const auto v_iljl = raster_[i_left + j_left * raster_size_[0]];\n      const auto v_irjl = raster_[i_right + j_left * raster_size_[0]];\n\n      const auto v_iljr = raster_[i_left + j_right * raster_size_[0]];\n      const auto v_irjr = raster_[i_right + j_right * raster_size_[0]];\n\n      const auto v_jl = v_iljl * (1. - i_ratio) + v_irjl * i_ratio;\n      const auto v_jr = v_iljr * (1. - i_ratio) + v_irjr * i_ratio;\n\n      return height_scaling_ * (v_jl * (1. - j_ratio) + v_jr * j_ratio);\n    }\n\n    /*\n     * Return the affine transformation information that is stored in the\n     * GeoTIFF image.\n     */\n    ACCESSOR_READ_ONLY(affine_transformation);\n\n    ACCESSOR_READ_ONLY(raster_size);\n    ACCESSOR_READ_ONLY(raster_offset);\n    ACCESSOR_READ_ONLY(height_scaling);\n\n  private:\n    void read_in_raster() const\n    {\n#ifdef WITH_GDAL\n      auto dataset_handle = GDALOpen(filename_.c_str(), GA_ReadOnly);\n      AssertThrow(dataset_handle,\n                  dealii::ExcMessage(\"GDAL error: file not found\"));\n\n      auto dataset = GDALDataset::FromHandle(dataset_handle);\n      Assert(dataset, dealii::ExcInternalError());\n\n      const auto driver = dataset->GetDriver();\n\n      driver_name_ = driver->GetMetadataItem(GDAL_DMD_LONGNAME);\n      if (dataset->GetProjectionRef() != nullptr)\n        driver_projection_ = dataset->GetProjectionRef();\n\n      /* For now we support one raster in the dataset: */\n\n      AssertThrow(\n          dataset->GetRasterCount() == 1,\n          dealii::ExcMessage(\n              \"GDAL driver error: currently we only support one raster\"));\n\n      const auto raster_band = dataset->GetRasterBand(1);\n\n      AssertThrow(dataset->GetRasterXSize() == raster_band->GetXSize() &&\n                      dataset->GetRasterYSize() == raster_band->GetYSize(),\n                  dealii::ExcMessage(\n                      \"GDAL driver error: the raster band has a different \"\n                      \"dimension than the (global) raster dimension of the \"\n                      \"geotiff image. This is not supported.\"));\n\n      /*\n       * FIXME: For now, we simply read in the entire geotiff file on\n       * each rank. In order to save memory for very large files it would\n       * be possible to create a bounding box for the all active cells of\n       * the triangulation and then only read in a small region for which\n       * we actually need data.\n       */\n\n      raster_offset_ = {0, 0};\n      raster_size_ = {dataset->GetRasterXSize(), dataset->GetRasterYSize()};\n\n      raster_.resize(raster_size_[0] * raster_size_[1]);\n      const auto error_code = raster_band->RasterIO(\n          GF_Read,\n          raster_offset_[0], /* x-offset of image region */\n          raster_offset_[1], /* y-offset of image region */\n          raster_size_[0],   /* x-size of image region */\n          raster_size_[1],   /* y-size of image region */\n          raster_.data(),\n          raster_size_[0], /* x-size of target buffer */\n          raster_size_[1], /* y-size of target buffer */\n          GDT_Float32,\n          0,\n          0);\n\n      AssertThrow(error_code == 0,\n                  dealii::ExcMessage(\n                      \"GDAL driver error: error reading in geotiff file\"));\n\n      /*\n       * Read in the affine transformation from the geotiff image.\n       *\n       * Note that this transformation differs from the one we use in the\n       * parameter file: GDAL uses typical image orientation: the origin\n       * of the dataset is in the \"top left\" corner (instead of bottom\n       * left) and the first (column) index goes to the right and the\n       * second (row) index goes down.\n       */\n\n      if (transformation_use_geotiff_) {\n        const auto success =\n            dataset->GetGeoTransform(affine_transformation_.data()) == CE_None;\n        AssertThrow(success,\n                    dealii::ExcMessage(\"GDAL driver error: no geo transform \"\n                                       \"present in geotiff file\"));\n      } else {\n        affine_transformation_ = transformation_;\n        /* Flip sign for j index (y-coordinate): */\n        affine_transformation_[2] *= -1.;\n        affine_transformation_[5] *= -1.;\n      }\n\n      /*\n       * Ensure that (i=0, j=raster_size[1]-1) corresponds to the user\n       * supplied (transformation_[0], transformation_[3]).\n       */\n      if (transformation_use_geotiff_ == false ||\n          transformation_use_geotiff_origin_ == false) {\n        const auto j_max = raster_size_[1] - 1;\n        affine_transformation_[0] =\n            transformation_[0] - j_max * affine_transformation_[2];\n        affine_transformation_[3] =\n            transformation_[3] - j_max * affine_transformation_[5];\n      }\n\n      /*\n       * Compute inverse transformation of\n       *\n       *    x = t[0] + t[1] * i + t[2] * j, y = t[3] + t[4] * i + t[5] * j.\n       *\n       * namely:\n       *\n       *     i =  it[1] * (x - it[0]) + it[2] * (y - it[3])\n       *     j =  it[4] * (x - it[0]) + it[5] * (y - it[3])\n       */\n      inverse_affine_transformation_[0] = affine_transformation_[0];\n      inverse_affine_transformation_[3] = affine_transformation_[3];\n\n      const auto determinant =\n          affine_transformation_[1] * affine_transformation_[5] -\n          affine_transformation_[2] * affine_transformation_[4];\n      const auto inv = 1. / determinant;\n      inverse_affine_transformation_[1] = inv * affine_transformation_[5];\n      inverse_affine_transformation_[2] = inv * (-affine_transformation_[2]);\n      inverse_affine_transformation_[4] = inv * (-affine_transformation_[4]);\n      inverse_affine_transformation_[5] = inv * affine_transformation_[1];\n\n      GDALClose(dataset_handle);\n\n#ifdef DEBUG_OUTPUT\n      std::cout << std::setprecision(16);\n      std::cout << \"GDAL: driver name    = \" << driver_name_;\n      std::cout << \"\\nGDAL: projection     = \" << driver_projection_;\n      std::cout << \"\\nGDAL: transformation =\";\n      for (const auto &it : affine_transformation_)\n        std::cout << \" \" << it;\n      std::cout << \"\\nGDAL: inverse trafo =\";\n      for (const auto &it : inverse_affine_transformation_)\n        std::cout << \" \" << it;\n      std::cout << \"\\nGDAL: raster offset  =\";\n      for (const auto &it : raster_offset_)\n        std::cout << \" \" << it;\n      std::cout << \"\\nGDAL: raster size    =\";\n      for (const auto &it : raster_size_)\n        std::cout << \" \" << it;\n      std::cout << std::endl;\n#endif\n\n      if (height_normalization_ != HeightNormalization::none) {\n        float shift = 0.;\n\n        if (height_normalization_ == HeightNormalization::minimum)\n          shift = *std::min_element(std::begin(raster_), std::end(raster_));\n        else if (height_normalization_ == HeightNormalization::maximum)\n          shift = *std::max_element(std::begin(raster_), std::end(raster_));\n        else {\n          Assert(height_normalization_ == HeightNormalization::average,\n                 dealii::ExcInternalError());\n          const auto sum = std::reduce(std::begin(raster_), std::end(raster_));\n          shift = sum / raster_.size();\n        }\n\n        std::for_each(std::begin(raster_),\n                      std::end(raster_),\n                      [&](auto &element) { element -= shift; });\n      }\n\n#else\n      static constexpr auto message =\n          \"ryujin has to be configured with GDAL support in order to read in \"\n          \"GeoTIFF images\";\n      AssertThrow(false, dealii::ExcMessage(message));\n      __builtin_trap();\n#endif\n    }\n\n\n    DEAL_II_ALWAYS_INLINE inline std::array<double, 2>\n    apply_transformation(const double i, const double j) const\n    {\n      const auto &at = affine_transformation_;\n      const double x = at[0] + at[1] * i + at[2] * j;\n      const double y = at[3] + at[4] * i + at[5] * j;\n      return {x, y};\n    }\n\n\n    DEAL_II_ALWAYS_INLINE inline std::array<double, 2>\n    apply_inverse_transformation(const double x, const double y) const\n    {\n      const auto &iat = inverse_affine_transformation_;\n      const double i = iat[1] * (x - iat[0]) + iat[2] * (y - iat[3]);\n      const double j = iat[4] * (x - iat[0]) + iat[5] * (y - iat[3]);\n      return {i, j};\n    }\n\n    /* Runtime parameters: */\n\n    std::string filename_;\n\n    std::array<double, 6> transformation_;\n    bool transformation_allow_out_of_bounds_queries_;\n    bool transformation_use_geotiff_;\n    bool transformation_use_geotiff_origin_;\n    HeightNormalization height_normalization_;\n    double height_scaling_;\n\n    /* GDAL data structures: */\n\n    //\n    // We use a Lazy<t> wrapper for lazy initialization with efficient\n    // Schmidt's double checking. We simply ignore the bool type here.\n    //\n    Lazy<bool> geotiff_guard_;\n    mutable std::string driver_name_;\n    mutable std::string driver_projection_;\n    mutable std::array<double, 6> affine_transformation_;\n    mutable std::array<double, 6> inverse_affine_transformation_;\n    mutable std::array<int, 2> raster_offset_;\n    mutable std::array<int, 2> raster_size_;\n    mutable std::vector<float> raster_;\n  };\n} // namespace ryujin\n"
  },
  {
    "path": "source/hyperbolic_module.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2024 by the ryujin authors\n//\n\n#include \"hyperbolic_module.template.h\"\n#include <instantiate.h>\n\n#define INSTANTIATE(dim, stages)                                               \\\n  template NUMBER HyperbolicModule<Description, dim, NUMBER>::step<stages>(    \\\n      const StateVector &,                                                     \\\n      std::array<std::reference_wrapper<const StateVector>, stages>,           \\\n      const std::array<NUMBER, stages>,                                        \\\n      StateVector &,                                                           \\\n      NUMBER,                                                                  \\\n      NUMBER) const\n\nnamespace ryujin\n{\n  /* instantiations */\n\n  template class HyperbolicModule<Description, 1, NUMBER>;\n  template class HyperbolicModule<Description, 2, NUMBER>;\n  template class HyperbolicModule<Description, 3, NUMBER>;\n\n  INSTANTIATE(1, 0);\n  INSTANTIATE(1, 1);\n  INSTANTIATE(1, 2);\n  INSTANTIATE(1, 3);\n  INSTANTIATE(1, 4);\n\n  INSTANTIATE(2, 0);\n  INSTANTIATE(2, 1);\n  INSTANTIATE(2, 2);\n  INSTANTIATE(2, 3);\n  INSTANTIATE(2, 4);\n\n  INSTANTIATE(3, 0);\n  INSTANTIATE(3, 1);\n  INSTANTIATE(3, 2);\n  INSTANTIATE(3, 3);\n  INSTANTIATE(3, 4);\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/hyperbolic_module.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"convenience_macros.h\"\n#include \"initial_values.h\"\n#include \"mpi_ensemble.h\"\n#include \"observer_pointer.h\"\n#include \"offline_data.h\"\n#include \"sparse_matrix.h\"\n#include \"state_vector.h\"\n\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/timer.h>\n#include <deal.II/lac/sparse_matrix.templates.h>\n#include <deal.II/lac/vector.h>\n\n#include <functional>\n\nnamespace ryujin\n{\n  /**\n   * An enum controlling the behavior on detection of an invariant domain\n   * or CFL violation. Such a case might occur for either aggressive CFL\n   * numbers > 1, and/or later stages in the Runge Kutta scheme when the\n   * time step tau is prescribed.\n   *\n   * The invariant domain violation is detected in the limiter and implies\n   * that the low-order update itself lies outside the invariant domain.\n   *\n   * A \"CFL violation\" occurs if the computed tau_max of a substep is\n   * significantly larger than the previously computed time step size tau\n   * with which we perform the update.\n   *\n   * @note Data structures in HyperbolicModule are initialized with the\n   * ensemble subrange communicator stored in MPIEnsemble. However, the\n   * time step size constraint (i.e. tau_max) is synchronized over the\n   * entire global communicator.\n   *\n   * @ingroup HyperbolicModule\n   */\n  enum class IDViolationStrategy : std::uint8_t {\n    /**\n     * Warn about an invariant domain violation but take no further\n     * action.\n     */\n    warn,\n\n    /**\n     * Raise a Restart exception on domain violation. This exception can be\n     * caught in TimeIntegrator and various different actions (adapt CFL\n     * and retry) can be taken depending on chosen strategy.\n     */\n    raise_exception,\n  };\n\n\n  /**\n   * A class signalling a restart, thrown in HyperbolicModule::single_step\n   * and caught in the TimeIntegrator when performing a time step. We\n   * currently signal a restart whenever we encounter an invariant domain\n   * violation of the low-order update.\n   *\n   * This might happen, for example, in subsequent invocations of step() in\n   * high-order time stepping algorithms when the time step size has to be\n   * kept constant. A standard strategy implemented in TimeIntegrator is to\n   * simply fall back to a smaller relative CFL number and try again.\n   *\n   * The field @p suggested_tau_max is set to a tau_max value with which\n   * the entire update step should be restarted.\n   *\n   * @ingroup TimeLoop\n   */\n  class Restart final\n  {\n  public:\n    double suggested_tau_max;\n  };\n\n\n  /**\n   * A class signalling that a correction step is required for the update.\n   * This is meant to be a truly exceptional situation, for example where\n   * the high-order update violates an admissibility condition. With our\n   * limiting approach this must never happen, ... but we might have taken\n   * some short cuts in the parabolic update that under rare circumstances\n   * require limiting.\n   *\n   * This exception is currently only used internally in the NavierStokes\n   * solver.\n   *\n   * @ingroup TimeLoop\n   */\n  class Correction final\n  {\n  };\n\n\n  /**\n   * Explicit forward Euler time-stepping for hyperbolic systems with\n   * convex limiting.\n   *\n   * This module is described in detail in @cite ryujin-2021-1, Alg. 1.\n   *\n   * @ingroup HyperbolicModule\n   */\n  template <typename Description, int dim, typename Number = double>\n  class HyperbolicModule final : public dealii::ParameterAcceptor\n  {\n  public:\n    /**\n     * @name Typedefs and constexpr constants\n     */\n    //@{\n\n    using HyperbolicSystem = typename Description::HyperbolicSystem;\n\n    using View =\n        typename Description::template HyperbolicSystemView<dim, Number>;\n\n    static constexpr auto problem_dimension = View::problem_dimension;\n\n    using state_type = typename View::state_type;\n\n    static constexpr auto n_precomputed_values = View::n_precomputed_values;\n\n    using precomputed_type = typename View::precomputed_type;\n\n    using initial_precomputed_type = typename View::initial_precomputed_type;\n\n    using StateVector = typename View::StateVector;\n\n    using InitialPrecomputedVector = typename View::InitialPrecomputedVector;\n\n    //@}\n    /**\n     * @name Constructor and setup\n     */\n    //@{\n\n    /**\n     * Constructor\n     */\n    HyperbolicModule(\n        const MPIEnsemble &mpi_ensemble,\n        std::map<std::string, dealii::Timer> &computing_timer,\n        const OfflineData<dim, Number> &offline_data,\n        const HyperbolicSystem &hyperbolic_system,\n        const InitialValues<Description, dim, Number> &initial_values,\n        const std::string &subsection = \"/HyperbolicModule\");\n\n    /**\n     * Prepare time stepping. A call to @p prepare() allocates temporary\n     * storage and is necessary before any of the following time-stepping\n     * functions can be called.\n     */\n    void prepare();\n\n    //@}\n    /**\n     * @name Functons for performing explicit time steps\n     */\n    //@{\n\n    /**\n     * (Re)initialize the hyperbolic state vector component and the\n     * precomputed vector component of the state vector.\n     *\n     * @note This routine does not modify the parabolic state vector component.\n     */\n    void reinit_state_vector(StateVector &state_vector) const;\n\n    /**\n     * This function preprocesses a given state vector @p U in preparation\n     * for an explicit euler step performed by the step() function. The\n     * function performs the following tasks:\n     *\n     *  - We update the @p U component of the state vector by enforcing\n     *    boundary conditions for the supplied time time @p t. We then\n     *    updates ghost ranges on @p U so that the state vector is\n     *    consistent across MPI ranks.\n     *\n     *  - The function then runs the precomputation loop that populates the\n     *    \"precomputed values\" component of the state vector and\n     *    distributes the result over all MPI ranks by updating ghost\n     *    ranges of the precomputed values vector.\n     */\n    void prepare_state_vector(StateVector &state_vector, Number t) const;\n\n    /**\n     * Given a reference to a previous state vector @p old_U perform an\n     * explicit euler step (and store the result in @p new_U). The\n     * function returns the chosen time step size tau with which the update\n     * was performed.\n     *\n     * The time step is performed with either tau_max (if @p tau is set to\n     * 0), or tau (if @p tau is nonzero). Here, tau_max is the minimum of\n     * the specified parameter @p tau_max and the computed maximal time\n     * step size according to the CFL condition. @p tau is the last\n     * parameter of the function.\n     *\n     * The function takes an optional array of states @p stage_U together\n     * with a an array of weights @p stage_weights to construct a\n     * modified high-order flux. The standard high-order flux reads\n     * (cf @cite ryujin-2021-1, Eq. 12):\n     * \\f{align}\n     *   \\newcommand{\\bF}{{\\boldsymbol F}}\n     *   \\newcommand{\\bU}{{\\boldsymbol U}}\n     *   \\newcommand\\bUni{\\bU^n_i}\n     *   \\newcommand\\bUnj{\\bU^n_j}\n     *   \\newcommand{\\polf}{{\\mathbb f}}\n     *   \\newcommand\\Ii{\\mathcal{I}(i)}\n     *   \\newcommand{\\bc}{{\\boldsymbol c}}\n     *   \\sum_{j\\in\\Ii} \\frac{m_{ij}}{m_{j}}\n     *   \\;\n     *   \\frac{m_{j}}{\\tau_n}\\big(\n     *   \\tilde\\bU_j^{H,n+1} - \\bU_j^{n}\\big)\n     *   \\;=\\;\n     *   \\bF^n_i + \\sum_{j\\in\\Ii}d_{ij}^{H,n}\\big(\\bUnj-\\bUni\\big),\n     *   \\qquad\\text{with}\\quad\n     *   \\bF^n_i\\;:=\\;\n     *   \\sum_{j\\in\\Ii}\\Big(-(\\polf(\\bUni)+\\polf(\\bUnj)) \\cdot\\bc_{ij}\\Big).\n     * \\f}\n     * Instead, the function assembles the modified high-order flux:\n     * \\f{align}\n     *   \\newcommand{\\bF}{{\\boldsymbol F}}\n     *   \\newcommand{\\bU}{{\\boldsymbol U}}\n     *   \\newcommand\\bUnis{\\bU^{s,n}_i}\n     *   \\newcommand\\bUnjs{\\bU^{s,n}_j}\n     *   \\newcommand{\\polf}{{\\mathbb f}}\n     *   \\newcommand\\Ii{\\mathcal{I}(i)}\n     *   \\newcommand{\\bc}{{\\boldsymbol c}}\n     *   \\tilde{\\bF}^n_i\\;:=\\;\n     *   \\big(1-\\sum_{s=\\{1:\\text{stages}\\}}\\omega_s\\big)\\bF^n_i\n     *   \\;+\\;\n     *   \\sum_{s=\\{1:stages\\}}\\omega_s \\bF^{s,n}_i\n     *   \\qquad\\text{with}\\quad\n     *   \\bF^{s,n}_i\\;:=\\;\n     *   \\sum_{j\\in\\Ii}\\Big(-(\\polf(\\bUnis)+\\polf(\\bUnjs)) \\cdot\\bc_{ij}\\Big).\n     * \\f}\n     * where \\f$\\omega_s\\f$ denotes the weigths for the given stages\n     * \\f$\\bU^{s,n}\\f$.\n     *\n     * @note The routine only performs an explicit update step on the\n     * locally owned dof index range. It neither updates the precomputed\n     * block of the state vector, sets boundary conditions (prior) to the\n     * update step, nor automatically updates the ghost range of the\n     * vector. It is thus necessary to call\n     * HyperbolicModule::prepare_state_vector() on @p old_state_vector\n     * prior to calling the step function.\n     */\n    template <int stages>\n    Number step(const StateVector &old_state_vector,\n                std::array<std::reference_wrapper<const StateVector>, stages>\n                    stage_state_vectors,\n                const std::array<Number, stages> stage_weights,\n                StateVector &new_state_vector,\n                Number tau = Number(0.),\n                Number tau_max = std::numeric_limits<Number>::max()) const;\n\n    /**\n     * Sets the relative CFL number used for computing an appropriate\n     * time-step size to the given value. The CFL number must be a positive\n     * value. If chosen to be within the interval \\f$(0,1)\\f$ then the\n     * low-order update and limiting stages guarantee invariant domain\n     * preservation.\n     */\n    void set_cfl(Number new_cfl) const\n    {\n      Assert(cfl_ > Number(0.), dealii::ExcInternalError());\n      cfl_ = new_cfl;\n    }\n\n    /**\n     * Returns the relative CFL number used for computing an appropriate\n     * time-step size.\n     */\n    ACCESSOR_READ_ONLY(cfl)\n\n    /**\n     * Sets the relative maximal acceptable tau_max ratio. If the ratio of\n     * the enforced time-step size tau and our computed tau_max for the\n     * hyperbolic (sub) step is above this limit we throw a Restart\n     * exception.\n     */\n    void set_acceptable_tau_max_ratio(Number new_acceptable_tau_max_ratio) const\n    {\n      Assert(new_acceptable_tau_max_ratio >= Number(1.),\n             dealii::ExcInternalError());\n      acceptable_tau_max_ratio_ = new_acceptable_tau_max_ratio;\n    }\n\n    /**\n     * Returns the relative maximal acceptable tau_max ratio. If the ratio\n     * of the enforced time-step size tau and our computed tau_max for the\n     * hyperbolic (sub) step is above this limit we throw a Restart\n     * exception.\n     */\n    ACCESSOR_READ_ONLY(acceptable_tau_max_ratio)\n\n    /**\n     * Sets the invariant domain violation strategy.\n     */\n    void set_id_violation_strategy(const IDViolationStrategy &strategy) const\n    {\n      id_violation_strategy_ = strategy;\n    }\n\n    /**\n     * Return a reference to the OfflineData object\n     */\n    ACCESSOR_READ_ONLY(offline_data)\n\n    /**\n     * Return a reference to the HyperbolicSystem object\n     */\n    ACCESSOR_READ_ONLY(hyperbolic_system)\n\n    /**\n     * Return a reference to the precomputed initial data vector\n     */\n    ACCESSOR_READ_ONLY(initial_precomputed)\n\n    /**\n     * Return a reference to alpha vector storing indicator values. Note\n     * that the values stored in alpha correspond to the last step executed\n     * by this class.\n     */\n    ACCESSOR_READ_ONLY(alpha)\n\n    /**\n     * The number of restarts signalled by the step() function. We signal a\n     * restart whenever we encounter an ID violation in the low order\n     * update.\n     */\n    ACCESSOR_READ_ONLY(n_restarts)\n\n    /**\n     * The number of corrections performed by the step() function. This\n     * function exists to mirror the ParabolicModule interface and will\n     * always return 0.\n     */\n    ACCESSOR_READ_ONLY(n_corrections)\n\n    /**\n     * The number of ID violation warnings encounterd in the step()\n     * function. We issue a warning whenever we encounter an ID violation\n     * in the low order update (and throwing a restart is disabled).\n     */\n    ACCESSOR_READ_ONLY(n_warnings)\n\n  private:\n    //@}\n    /**\n     * @name Run time options\n     */\n    //@{\n    typename Description::template Indicator<dim, Number>::Parameters\n        indicator_parameters_;\n\n    typename Description::template Limiter<dim, Number>::Parameters\n        limiter_parameters_;\n\n    typename Description::template RiemannSolver<dim, Number>::Parameters\n        riemann_solver_parameters_;\n\n    //@}\n    /**\n     * @name Internal data\n     */\n    //@{\n\n    const MPIEnsemble &mpi_ensemble_;\n    std::map<std::string, dealii::Timer> &computing_timer_;\n\n    dealii::ObserverPointer<const OfflineData<dim, Number>> offline_data_;\n    dealii::ObserverPointer<const HyperbolicSystem> hyperbolic_system_;\n    dealii::ObserverPointer<const InitialValues<Description, dim, Number>>\n        initial_values_;\n\n    mutable Number cfl_;\n    mutable Number acceptable_tau_max_ratio_;\n\n    mutable IDViolationStrategy id_violation_strategy_;\n\n    mutable unsigned int n_restarts_;\n    mutable unsigned int n_corrections_;\n    mutable unsigned int n_warnings_;\n\n    InitialPrecomputedVector initial_precomputed_;\n\n    using ScalarVector = typename Vectors::ScalarVector<Number>;\n    mutable ScalarVector alpha_;\n\n    static constexpr auto n_bounds =\n        Description::template Limiter<dim, Number>::n_bounds;\n    mutable Vectors::MultiComponentVector<Number, n_bounds> bounds_;\n\n    using HyperbolicVector =\n        Vectors::MultiComponentVector<Number, problem_dimension>;\n    mutable HyperbolicVector r_;\n\n    mutable SparseMatrix<Number> dij_matrix_;\n    mutable SparseMatrix<Number> lij_matrix_;\n    mutable SparseMatrix<Number> lij_matrix_next_;\n    mutable SparseMatrix<Number, problem_dimension> pij_matrix_;\n\n    //@}\n  };\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/hyperbolic_module.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include \"hyperbolic_module.h\"\n#include \"loop.h\"\n#include \"mpi_ensemble.h\"\n#include \"scope.h\"\n#include \"simd.h\"\n\n#include <atomic>\n\nnamespace ryujin\n{\n  namespace ShallowWater\n  {\n    struct Description;\n  }\n\n  using namespace dealii;\n\n  template <typename Description, int dim, typename Number>\n  HyperbolicModule<Description, dim, Number>::HyperbolicModule(\n      const MPIEnsemble &mpi_ensemble,\n      std::map<std::string, dealii::Timer> &computing_timer,\n      const OfflineData<dim, Number> &offline_data,\n      const HyperbolicSystem &hyperbolic_system,\n      const InitialValues<Description, dim, Number> &initial_values,\n      const std::string &subsection /*= \"HyperbolicModule\"*/)\n      : ParameterAcceptor(subsection)\n      , indicator_parameters_(subsection + \"/indicator\")\n      , limiter_parameters_(subsection + \"/limiter\")\n      , riemann_solver_parameters_(subsection + \"/riemann solver\")\n      , mpi_ensemble_(mpi_ensemble)\n      , computing_timer_(computing_timer)\n      , offline_data_(&offline_data)\n      , hyperbolic_system_(&hyperbolic_system)\n      , initial_values_(&initial_values)\n      , cfl_(0.2)\n      , acceptable_tau_max_ratio_(1.e6)\n      , id_violation_strategy_(IDViolationStrategy::warn)\n      , n_restarts_(0)\n      , n_corrections_(0)\n      , n_warnings_(0)\n  {\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void HyperbolicModule<Description, dim, Number>::prepare()\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"HyperbolicModule<Description, dim, Number>::prepare()\"\n              << std::endl;\n#endif\n\n    AssertThrow(limiter_parameters_.iterations() <= 2,\n                dealii::ExcMessage(\n                    \"The number of limiter iterations must be between [0,2]\"));\n\n    /* Initialize vectors: */\n\n    const auto &scalar_partitioner = offline_data_->scalar_partitioner();\n    alpha_.reinit_with_scalar_partitioner(scalar_partitioner);\n    bounds_.reinit_with_scalar_partitioner(scalar_partitioner);\n    r_.reinit_with_vector_partitioner(\n        offline_data_->hyperbolic_vector_partitioner());\n\n    /* Initialize matrices: */\n\n    const auto &sparsity_simd = offline_data_->sparsity_pattern_simd();\n    dij_matrix_.reinit(sparsity_simd);\n    lij_matrix_.reinit(sparsity_simd);\n    lij_matrix_next_.reinit(sparsity_simd);\n    pij_matrix_.reinit(sparsity_simd);\n\n    /* Set up initial precomputed vector: */\n\n    initial_precomputed_ =\n        initial_values_->interpolate_initial_precomputed_vector();\n  }\n\n\n  /*\n   * -------------------------------------------------------------------------\n   * Step 0: Reinitialize vector\n   * -------------------------------------------------------------------------\n   */\n\n\n  /**\n   * Helper function that (re)initializes the state and the precomputed\n   * component of a StateVector to proper sizes.\n   *\n   * @note This method does neither initialize nor resize the parabolic\n   * state vector component. The ParabolicModule itself has to ensure\n   * proper setup during prepare_state_vector() and solve().\n   */\n  template <typename Description, int dim, typename Number>\n  void HyperbolicModule<Description, dim, Number>::reinit_state_vector(\n      StateVector &state_vector) const\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"HyperbolicModule<dim, Number>::reinit_state_vector()\"\n              << std::endl;\n#endif\n\n    auto &[U, precomputed, V] = state_vector;\n    U.reinit_with_vector_partitioner(\n        offline_data_->hyperbolic_vector_partitioner());\n    precomputed.reinit_with_vector_partitioner(\n        offline_data_->precomputed_vector_partitioner());\n\n#ifdef DEBUG\n    /* Poison all vectors: */\n    using state_type = typename View::state_type;\n\n    constexpr auto nan = std::numeric_limits<Number>::signaling_NaN();\n\n    const unsigned int n_owned = offline_data_->n_locally_owned();\n    for (unsigned int i = 0; i < n_owned; ++i) {\n      U.write_tensor(state_type{} * nan, i);\n      precomputed.write_tensor(\n          dealii::Tensor<1, n_precomputed_values, Number>() * nan, i);\n    }\n#endif\n  }\n\n\n  /*\n   * -------------------------------------------------------------------------\n   * Step 1: Apply boundary conditions and precompute values\n   * -------------------------------------------------------------------------\n   */\n\n\n  template <typename Description, int dim, typename Number>\n  void HyperbolicModule<Description, dim, Number>::prepare_state_vector(\n      StateVector &state_vector, Number t) const\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"HyperbolicModule<Description, dim, \"\n                 \"Number>::prepare_state_vector()\"\n              << std::endl;\n#endif\n\n    auto &U = std::get<0>(state_vector);\n\n    const auto &boundary_map = offline_data_->boundary_map();\n    using VA = VectorizedArray<Number>;\n\n    Scope scope(computing_timer_,\n                \"time step [H] 1 - update boundary values, precompute values\");\n\n    /* FIXME: not thread parallel... */\n    for (const auto &entry : boundary_map) {\n      const auto &[i, normal, normal_mass, boundary_mass, id, position] = entry;\n\n      /*\n       * Relay the task of applying appropriate boundary conditions to the\n       * Problem Description.\n       */\n\n      if (id == Boundary::do_nothing)\n        continue;\n\n      auto U_i = U.read_tensor(i);\n\n      /* Use a lambda to avoid computing unnecessary state values */\n      auto get_dirichlet_data = [position = position, t = t, this]() {\n        return initial_values_->initial_state(position, t);\n      };\n\n      const auto view = hyperbolic_system_->template view<dim, Number>();\n      U_i = view.apply_boundary_conditions(id, U_i, normal, get_dirichlet_data);\n      U.write_tensor(U_i, i);\n    }\n\n    U.update_ghost_values();\n\n    /*\n     * Compute and populate precomputed values.\n     */\n\n    hyperbolic_system_->fill_precomputed_values(*offline_data_, state_vector);\n\n    auto &precomputed = std::get<1>(state_vector);\n    precomputed.update_ghost_values();\n  }\n\n\n  /*\n   * -------------------------------------------------------------------------\n   * Step 2 - 7: Perform an explicit Euler step\n   * -------------------------------------------------------------------------\n   */\n\n\n  namespace\n  {\n    /**\n     * Internally used: returns true if all indices are on the lower\n     * triangular part of the matrix.\n     */\n    template <typename T>\n    bool all_below_diagonal(unsigned int i, const unsigned int *js)\n    {\n      if constexpr (std::is_same_v<T, typename get_value_type<T>::type>) {\n        /* Non-vectorized sequential access. */\n        const auto j = *js;\n        return j < i;\n\n      } else {\n        /* Vectorized fast access. index must be divisible by simd_length */\n\n        constexpr auto simd_length = T::size();\n\n        bool all_below_diagonal = true;\n        for (unsigned int k = 0; k < simd_length; ++k)\n          if (js[k] >= i + k) {\n            all_below_diagonal = false;\n            break;\n          }\n        return all_below_diagonal;\n      }\n    }\n  } // namespace\n\n\n  template <typename Description, int dim, typename Number>\n  template <int stages>\n  Number HyperbolicModule<Description, dim, Number>::step(\n      const StateVector &old_state_vector,\n      std::array<std::reference_wrapper<const StateVector>, stages>\n          stage_state_vectors,\n      const std::array<Number, stages> stage_weights,\n      StateVector &new_state_vector,\n      Number tau /*= 0.*/,\n      Number tau_max /*= std::numeric_limits<Number>::max()*/) const\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"HyperbolicModule<Description, dim, Number>::step()\"\n              << std::endl;\n#endif\n\n    auto &old_U = std::get<0>(old_state_vector);\n    auto &old_precomputed = std::get<1>(old_state_vector);\n    auto &new_U = std::get<0>(new_state_vector);\n\n    /*\n     * Workaround: A constexpr boolean storing the fact whether we\n     * instantiate the HyperbolicModule for the shallow water equations.\n     *\n     * Rationale: Currently, the shallow water equations is the only\n     * hyperbolic system for which we have to (a) form equilibrated states\n     * for the low-order update, and (b) apply an affine shift for\n     * computing limiter bounds. It's not so easy to come up with a\n     * meaningful abstraction layer for this (in particular because we only\n     * have one PDE). Thus, for the time being we simply special case a\n     * small amount of code in this routine.\n     *\n     * FIXME: Refactor into a proper abstraction layer / interface.\n     */\n    constexpr bool shallow_water =\n        std::is_same_v<Description, ShallowWater::Description>;\n\n    using VA = VectorizedArray<Number>;\n\n    /* Index ranges for the iteration over the sparsity pattern : */\n\n    constexpr auto simd_length = VA::size();\n    const unsigned int n_internal = offline_data_->n_locally_internal();\n    const unsigned int n_owned = offline_data_->n_locally_owned();\n\n    /* References to precomputed matrices and the stencil: */\n\n    const auto &sparsity_simd = offline_data_->sparsity_pattern_simd();\n\n    const auto &mass_matrix = offline_data_->mass_matrix();\n    const auto &mass_matrix_inverse = offline_data_->mass_matrix_inverse();\n    const auto &lumped_mass_matrix = offline_data_->lumped_mass_matrix();\n    const auto &lumped_mass_matrix_inverse =\n        offline_data_->lumped_mass_matrix_inverse();\n\n    const auto &cij_matrix = offline_data_->cij_matrix();\n    const auto &incidence_matrix = offline_data_->incidence_matrix();\n\n    const auto &coupling_boundary_pairs =\n        offline_data_->coupling_boundary_pairs();\n\n    const Number measure_of_omega_inverse =\n        Number(1.) / offline_data_->measure_of_omega();\n\n    /*\n     * Lambdas for creating the computing timer and loop strings:\n     */\n\n    int step_no = 1;\n\n    const auto scoped_name = [&step_no](const auto &name,\n                                        const bool advance = true) {\n      advance || step_no--;\n      return \"time step [H] \" + std::to_string(++step_no) + \" - \" + name;\n    };\n\n    const auto loop_name = [&step_no]() {\n      return \"time_step_\" + std::to_string(step_no);\n    };\n\n    /* A boolean signalling that a restart is necessary: */\n    std::atomic<bool> restart_needed = false;\n\n    /*\n     * -------------------------------------------------------------------------\n     * Step 2: Compute off-diagonal d_ij, and alpha_i\n     *\n     * The computation of the d_ij is quite costly. So we do a trick to\n     * save a bit of computational resources. Instead of computing all d_ij\n     * entries for a row of a given local index i, we only compute d_ij for\n     * which j > i,\n     *\n     *        llllrr\n     *      l .xxxxx\n     *      l ..xxxx\n     *      l ...xxx\n     *      l ....xx\n     *      r ......\n     *      r ......\n     *\n     *  and symmetrize in Step 2.\n     *\n     *  MM: We could save a bit more computational resources by only\n     *  computing entries for which *IN A GLOBAL* enumeration j > i. But\n     *  the index translation, subsequent symmetrization, and exchange\n     *  sounds a bit too expensive...\n     * -------------------------------------------------------------------------\n     */\n    {\n      Scope scope(computing_timer_, scoped_name(\"compute d_ij, and alpha_i\"));\n\n      const auto body = [&](auto sentinel, unsigned int i) {\n        using T = decltype(sentinel);\n        constexpr unsigned int stride_size = get_stride_size<T>;\n\n        using RiemannSolver =\n            typename Description::template RiemannSolver<dim, T>;\n        RiemannSolver riemann_solver(\n            *hyperbolic_system_, riemann_solver_parameters_, old_precomputed);\n\n        using Indicator = typename Description::template Indicator<dim, T>;\n        Indicator indicator(\n            *hyperbolic_system_, indicator_parameters_, old_precomputed);\n\n        /* Skip constrained degrees of freedom: */\n        const unsigned int row_length = sparsity_simd.row_length(i);\n        if (row_length == 1)\n          return;\n\n        const auto U_i = old_U.template read_tensor<T>(i);\n\n        indicator.reset(i, U_i);\n\n        const unsigned int *js = sparsity_simd.columns(i);\n        for (unsigned int col_idx = 0; col_idx < row_length;\n             ++col_idx, js += stride_size) {\n\n          const auto U_j = old_U.template read_tensor<T>(js);\n\n          const auto c_ij = cij_matrix.template read_tensor<T>(i, col_idx);\n\n          indicator.accumulate(js, U_j, c_ij);\n\n          /* Skip diagonal. */\n          if (col_idx == 0)\n            continue;\n\n          /* Only iterate over the upper triangular portion of d_ij */\n          if (all_below_diagonal<T>(i, js))\n            continue;\n\n          const auto norm = c_ij.norm();\n          const auto n_ij = c_ij / norm;\n          const auto lambda_max = riemann_solver.compute(U_i, U_j, i, js, n_ij);\n          const auto d_ij = norm * lambda_max;\n\n          dij_matrix_.write_entry(d_ij, i, col_idx, true);\n        }\n\n        const auto mass = lumped_mass_matrix.template read_entry<T>(i);\n        const auto hd_i = mass * measure_of_omega_inverse;\n        alpha_.template write_entry<T>(indicator.alpha(hd_i), i);\n      };\n\n      cpu_simd_loop<Number>(loop_name(), body, 0, n_internal, n_owned);\n\n      alpha_.update_ghost_values();\n    }\n\n    /*\n     * -------------------------------------------------------------------------\n     * Step 3: Compute diagonal of d_ij, and maximal time-step size.\n     * -------------------------------------------------------------------------\n     */\n\n    {\n      Scope scope(computing_timer_,\n                  scoped_name(\"compute bdry d_ij, diag d_ii, and tau_max\"));\n\n      /*\n       * Complete d_ij at boundary:\n       *\n       * Here, for continuous finite elements the assumption c_ij = -c_ji\n       * no longer holds true. This implies that d_ij != d_ji. We thus need\n       * to compute the lower-triangular part of d_ij, where i and j are\n       * boundary degrees of freedom as well.\n       */\n\n      /*\n       * Note: we need this dance of iterating over an integer and then\n       * accessing the element to make Apple's OpenMP implementation\n       * happy.\n       */\n      const auto body_boundary = [&](const auto &, const unsigned int k) {\n        const auto &[i, col_idx, j] = coupling_boundary_pairs[k];\n\n        using RiemannSolver =\n            typename Description::template RiemannSolver<dim, Number>;\n        RiemannSolver riemann_solver(\n            *hyperbolic_system_, riemann_solver_parameters_, old_precomputed);\n\n        /*\n         * Only work on index pairs \"i < j\" that point to the upper\n         * triangular portion of the d_ij matrix. For all of these index\n         * pairs we compute the corresponding d_ji entry and fix up the\n         * d_ij entry (from step 2) by taking the maximum. Note that we\n         * actually do not store anything in the d_ji entry itself because\n         * we symmetrize the matrix later on anyway.\n         */\n        if (j < i)\n          return;\n\n        const auto U_i = old_U.read_tensor(i);\n        const auto U_j = old_U.read_tensor(j);\n\n        const auto c_ji = cij_matrix.read_transposed_tensor(i, col_idx);\n        Assert(c_ji.norm() > 1.e-12, ExcInternalError());\n        const auto norm_ji = c_ji.norm();\n        const auto n_ji = c_ji / norm_ji;\n\n        const auto d_ij = dij_matrix_.read_entry(i, col_idx);\n\n        const auto lambda_max = riemann_solver.compute(U_j, U_i, j, &i, n_ji);\n        const auto d_ji = norm_ji * lambda_max;\n\n        dij_matrix_.write_entry(std::max(d_ij, d_ji), i, col_idx);\n      };\n\n      cpu_simd_loop<Number>(loop_name(),\n                            body_boundary,\n                            0,\n                            /*no vectorization*/ 0,\n                            coupling_boundary_pairs.size());\n\n      std::atomic<Number> local_tau_max = tau_max;\n\n      /* Symmetrize d_ij: */\n      const auto body = [&](auto, unsigned int i) {\n\n#ifdef DEBUG_SYMMETRY_CHECK\n        using RiemannSolver =\n            typename Description::template RiemannSolver<dim, Number>;\n        RiemannSolver riemann_solver(\n            *hyperbolic_system_, riemann_solver_parameters_, old_precomputed);\n#endif\n\n        /* Skip constrained degrees of freedom: */\n        const unsigned int row_length = sparsity_simd.row_length(i);\n        if (row_length == 1)\n          return;\n\n        Number d_sum = Number(0.);\n\n        /* skip diagonal: */\n        const unsigned int *js = sparsity_simd.columns(i);\n        for (unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {\n          const auto j =\n              *(i < n_internal ? js + col_idx * simd_length : js + col_idx);\n\n          // fill lower triangular part of dij_matrix missing from step 1\n          if (j < i) {\n            const auto d_ji = dij_matrix_.read_transposed_entry(i, col_idx);\n\n#ifdef DEBUG_SYMMETRY_CHECK\n            /* Verify that d_ji == std::max(d_ij, d_ji): */\n\n            const auto U_i = old_U.read_tensor(i);\n            const auto U_j = old_U.read_tensor(j);\n\n            const auto c_ij = cij_matrix.read_tensor(i, col_idx);\n            Assert(c_ij.norm() > 1.e-12, ExcInternalError());\n            const auto norm_ij = c_ij.norm();\n            const auto n_ij = c_ij / norm_ij;\n\n            const auto lambda_max =\n                riemann_solver.compute(U_i, U_j, i, &j, n_ij);\n            const auto d_ij = norm_ij * lambda_max;\n\n            Assert(d_ij <= d_ji + 1.0e-12,\n                   dealii::ExcMessage(\"d_ij not symmetrized correctly on \"\n                                      \"boundary degrees of freedom.\"));\n#endif\n\n            dij_matrix_.write_entry(d_ji, i, col_idx);\n          }\n\n          d_sum -= dij_matrix_.read_entry(i, col_idx);\n        }\n\n        /*\n         * Make sure that we do not accidentally divide by zero. (Yes, this\n         * can happen for some (admittedly, rather esoteric) scalar\n         * conservation equations...).\n         */\n        d_sum =\n            std::min(d_sum, Number(-1.e6) * std::numeric_limits<Number>::min());\n\n        /* write diagonal element */\n        dij_matrix_.write_entry(d_sum, i, 0);\n\n        const Number mass = lumped_mass_matrix.read_entry(i);\n        const Number local_tau = cfl_ * mass / (Number(-2.) * d_sum);\n\n        Number current_value = local_tau_max.load();\n        while (current_value > local_tau &&\n               !local_tau_max.compare_exchange_weak(current_value, local_tau))\n          ;\n      };\n\n      cpu_simd_loop<Number>(\n          loop_name(), body, 0, /*no vectorization*/ 0, n_owned);\n\n      tau_max = local_tau_max.load();\n    }\n\n    {\n      Scope scope(computing_timer_,\n                  \"time step [X] _ - synchronization barriers\");\n\n      /*\n       * MPI Barrier: Synchronize the maximal time-step size. This has to\n       * happen either over the global, or the local subrange communicator:\n       */\n      tau_max = Utilities::MPI::min(\n          tau_max, mpi_ensemble_.synchronization_communicator());\n\n      AssertThrow(\n          !std::isnan(tau_max) && !std::isinf(tau_max) && tau_max > 0.,\n          ExcMessage(\n              \"I'm sorry, Dave. I'm afraid I can't do that.\\nWe crashed.\"));\n\n      tau = (tau == Number(0.) ? tau_max : tau);\n\n#ifdef DEBUG_OUTPUT\n      std::cout << \"        computed tau_max = \" << tau_max\n                << \" (CFL = \" << cfl_ << \")\" << std::endl;\n      std::cout << \"        step with tau    = \" << tau << std::endl;\n#endif\n\n      /* We need to signal a restart if the enforced tau is too wacky: */\n      restart_needed = (tau > acceptable_tau_max_ratio_ * tau_max);\n\n      /* Don't bother with computing the update step, signal a restart: */\n      if (restart_needed &&\n          id_violation_strategy_ == IDViolationStrategy::raise_exception) {\n        n_restarts_++;\n        /* Suggest a restart with tau_max: */\n#ifdef DEBUG_OUTPUT\n        std::cout << \"        signalling restart (suggested_tau_max = \"\n                  << tau_max << \")\" << std::endl;\n#endif\n        throw Restart{tau_max};\n      }\n    }\n\n#ifdef DEBUG\n    /*  Exchange d_ij so that we can check for symmetry: */\n    dij_matrix_.update_ghost_rows();\n#endif\n\n    /*\n     * -------------------------------------------------------------------------\n     * Step 4: Low-order update, also compute limiter bounds, R_i\n     * -------------------------------------------------------------------------\n     */\n\n    {\n      Scope scope(computing_timer_,\n                  scoped_name(\"l.-o. update, compute bounds, r_i, and p_ij\"));\n\n      const Number weight =\n          -std::accumulate(stage_weights.begin(), stage_weights.end(), -1.);\n\n      auto body = [&](auto sentinel,\n                      auto have_discontinuous_ansatz,\n                      const unsigned int i) {\n        using T = decltype(sentinel);\n        using View =\n            typename Description::template HyperbolicSystemView<dim, T>;\n        using Limiter = typename Description::template Limiter<dim, T>;\n        using flux_contribution_type = typename View::flux_contribution_type;\n        using state_type = typename View::state_type;\n\n        constexpr unsigned int stride_size = get_stride_size<T>;\n\n        const auto view = hyperbolic_system_->template view<dim, T>();\n\n        Limiter limiter(\n            *hyperbolic_system_, limiter_parameters_, old_precomputed);\n\n        /* Skip constrained degrees of freedom: */\n        const unsigned int row_length = sparsity_simd.row_length(i);\n        if (row_length == 1)\n          return;\n\n        const auto U_i = old_U.template read_tensor<T>(i);\n        auto U_i_new = U_i;\n\n        const auto alpha_i = alpha_.template read_entry<T>(i);\n        const auto m_i = lumped_mass_matrix.template read_entry<T>(i);\n        const auto m_i_inv =\n            lumped_mass_matrix_inverse.template read_entry<T>(i);\n\n        const auto flux_i = view.flux_contribution(\n            old_precomputed, initial_precomputed_, i, U_i);\n\n        std::array<flux_contribution_type, stages> flux_iHs;\n        [[maybe_unused]] state_type S_iH;\n\n        for (int s = 0; s < stages; ++s) {\n          const auto &[U_s, prec_s, V_s] = stage_state_vectors[s].get();\n\n          const auto U_iHs = U_s.template read_tensor<T>(i);\n          flux_iHs[s] =\n              view.flux_contribution(prec_s, initial_precomputed_, i, U_iHs);\n\n          if constexpr (View::have_source_terms) {\n            S_iH += stage_weights[s] * view.nodal_source(prec_s, i, U_iHs, tau);\n          }\n        }\n\n        [[maybe_unused]] state_type S_i;\n        state_type F_iH;\n\n        if constexpr (View::have_source_terms) {\n          S_i = view.nodal_source(old_precomputed, i, U_i, tau);\n          S_iH += weight * S_i;\n          U_i_new += tau * /* m_i_inv * m_i */ S_i;\n          F_iH += m_i * S_iH;\n        }\n\n        limiter.reset(i, U_i, flux_i);\n\n        [[maybe_unused]] state_type affine_shift;\n\n        /*\n         * Workaround: For shallow water we need to accumulate an\n         * additional contribution to the affine shift over the stencil\n         * before we can compute limiter bounds.\n         */\n\n        const unsigned int *js = sparsity_simd.columns(i);\n        if constexpr (shallow_water) {\n          for (unsigned int col_idx = 0; col_idx < row_length;\n               ++col_idx, js += stride_size) {\n\n            const auto U_j = old_U.template read_tensor<T>(js);\n            const auto flux_j = view.flux_contribution(\n                old_precomputed, initial_precomputed_, js, U_j);\n\n            const auto d_ij = dij_matrix_.template read_entry<T>(i, col_idx);\n            const auto c_ij = cij_matrix.template read_tensor<T>(i, col_idx);\n\n            const auto B_ij = view.affine_shift(flux_i, flux_j, c_ij, d_ij);\n            affine_shift += B_ij;\n          }\n\n          affine_shift *= tau * m_i_inv;\n        }\n\n        if constexpr (View::have_source_terms) {\n          affine_shift += tau * /* m_i_inv * m_i */ S_i;\n        }\n\n        js = sparsity_simd.columns(i);\n        for (unsigned int col_idx = 0; col_idx < row_length;\n             ++col_idx, js += stride_size) {\n\n          const auto U_j = old_U.template read_tensor<T>(js);\n\n          const auto alpha_j = alpha_.template read_entry<T>(js);\n\n          const auto d_ij = dij_matrix_.template read_entry<T>(i, col_idx);\n          auto factor = (alpha_i + alpha_j) * Number(.5);\n\n          if constexpr (have_discontinuous_ansatz) {\n            const auto incidence_ij =\n                incidence_matrix.template read_entry<T>(i, col_idx);\n            factor = std::max(factor, incidence_ij);\n          }\n\n          const auto d_ijH = d_ij * factor;\n\n#ifdef DEBUG_SYMMETRY_CHECK\n          /*\n           * Verify that all local chunks of the d_ij matrix have been\n           * computed consistently over all MPI ranks. For that we import\n           * all ghost rows from neighboring MPI ranks and simply check\n           * that the (local) values of d_ij and d_ji match.\n           */\n          const auto d_ji =\n              dij_matrix_.template read_transposed_entry<T>(i, col_idx);\n          Assert(std::max(std::abs(d_ij - d_ji), T(1.0e-12)) == T(1.0e-12),\n                 dealii::ExcMessage(\n                     \"d_ij not symmetrized correctly over MPI ranks\"));\n#endif\n\n          const auto c_ij = cij_matrix.template read_tensor<T>(i, col_idx);\n          constexpr auto eps = std::numeric_limits<Number>::epsilon();\n          const auto scale =\n              dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(\n                  std::abs(d_ij), T(eps * eps), T(0.), T(1.) / d_ij);\n          const auto scaled_c_ij = c_ij * scale;\n\n          const auto flux_j = view.flux_contribution(\n              old_precomputed, initial_precomputed_, js, U_j);\n\n          const auto m_ij = mass_matrix.template read_entry<T>(i, col_idx);\n\n          /*\n           * Compute low-order flux and limiter bounds:\n           */\n\n          const auto flux_ij = view.flux_divergence(flux_i, flux_j, c_ij);\n          U_i_new += tau * m_i_inv * flux_ij;\n          auto P_ij = -flux_ij;\n\n          if constexpr (shallow_water) {\n            /*\n             * Workaround: Shallow water (and related) are special:\n             */\n\n            const auto &[U_star_ij, U_star_ji] =\n                view.equilibrated_states(flux_i, flux_j);\n\n            U_i_new += tau * m_i_inv * d_ij * (U_star_ji - U_star_ij);\n            F_iH += d_ijH * (U_star_ji - U_star_ij);\n            P_ij += (d_ijH - d_ij) * (U_star_ji - U_star_ij);\n\n            limiter.accumulate(\n                U_j, U_star_ij, U_star_ji, scaled_c_ij, affine_shift);\n\n          } else {\n\n            U_i_new += tau * m_i_inv * d_ij * (U_j - U_i);\n            F_iH += d_ijH * (U_j - U_i);\n            P_ij += (d_ijH - d_ij) * (U_j - U_i);\n\n            limiter.accumulate(js, U_j, flux_j, scaled_c_ij, affine_shift);\n          }\n\n          if constexpr (View::have_source_terms) {\n            F_iH -= m_ij * S_iH;\n            P_ij -= m_ij * /*sic!*/ S_i;\n          }\n\n          /*\n           * Compute high-order fluxes and source terms:\n           */\n\n          if constexpr (View::have_high_order_flux) {\n            const auto high_order_flux_ij =\n                view.high_order_flux_divergence(flux_i, flux_j, c_ij);\n            F_iH += weight * high_order_flux_ij;\n            P_ij += weight * high_order_flux_ij;\n          } else {\n            F_iH += weight * flux_ij;\n            P_ij += weight * flux_ij;\n          }\n\n          if constexpr (View::have_source_terms) {\n            const auto S_j = view.nodal_source(old_precomputed, js, U_j, tau);\n            F_iH += weight * m_ij * S_j;\n            P_ij += weight * m_ij * S_j;\n          }\n\n          for (int s = 0; s < stages; ++s) {\n            const auto &[U_s, prec_s, V_s] = stage_state_vectors[s].get();\n\n            const auto U_jHs = U_s.template read_tensor<T>(js);\n            const auto flux_jHs =\n                view.flux_contribution(prec_s, initial_precomputed_, js, U_jHs);\n\n            if constexpr (View::have_high_order_flux) {\n              const auto high_order_flux_ij =\n                  view.high_order_flux_divergence(flux_iHs[s], flux_jHs, c_ij);\n              F_iH += stage_weights[s] * high_order_flux_ij;\n              P_ij += stage_weights[s] * high_order_flux_ij;\n            } else {\n              const auto flux_ij =\n                  view.flux_divergence(flux_iHs[s], flux_jHs, c_ij);\n              F_iH += stage_weights[s] * flux_ij;\n              P_ij += stage_weights[s] * flux_ij;\n            }\n\n            if constexpr (View::have_source_terms) {\n              const auto S_js = view.nodal_source(prec_s, js, U_jHs, tau);\n              F_iH += stage_weights[s] * m_ij * S_js;\n              P_ij += stage_weights[s] * m_ij * S_js;\n            }\n          }\n\n          pij_matrix_.template write_tensor<T>(P_ij, i, col_idx, true);\n        }\n\n#ifdef DEBUG_EXPENSIVE_BOUNDS_CHECK\n        if (!view.is_admissible(U_i_new)) {\n          restart_needed = true;\n        }\n#endif\n\n        new_U.template write_tensor<T>(U_i_new, i);\n        r_.template write_tensor<T>(F_iH, i);\n\n        const auto hd_i = m_i * measure_of_omega_inverse;\n        const auto relaxed_bounds = limiter.bounds(hd_i);\n        bounds_.template write_tensor<T>(relaxed_bounds, i);\n      };\n\n      /*\n       * Chain through a compile time integral constant std::true_type for\n       * a discontinuous ansatz and std::false_type otherwise. We use the\n       * (constexpr) integral constant later on to avoid branching when\n       * computing d_ijH.\n       */\n      if (offline_data_->discretization().have_discontinuous_ansatz()) {\n        cpu_simd_loop<Number>(\n            loop_name(), body, 0, n_internal, n_owned, std::true_type{});\n      } else {\n        cpu_simd_loop<Number>(\n            loop_name(), body, 0, n_internal, n_owned, std::false_type{});\n      }\n\n      r_.update_ghost_values();\n      if (offline_data_->discretization().have_discontinuous_ansatz()) {\n        /*\n         * In case we extend bounds over the stencil, we have to ensure\n         * that ghost ranges are properly communicated over all MPI\n         * ranks.\n         */\n        bounds_.update_ghost_values();\n      }\n    }\n\n    /*\n     * -------------------------------------------------------------------------\n     * Step 5: Compute second part of P_ij, and l_ij (first round):\n     * -------------------------------------------------------------------------\n     */\n\n    if (limiter_parameters_.iterations() != 0) {\n      Scope scope(computing_timer_, scoped_name(\"compute p_ij, and l_ij\"));\n\n      auto body = [&](auto sentinel,\n                      auto have_discontinuous_ansatz,\n                      const unsigned int i) {\n        using T = decltype(sentinel);\n        using View =\n            typename Description::template HyperbolicSystemView<dim, T>;\n        using Limiter = typename Description::template Limiter<dim, T>;\n\n        constexpr unsigned int stride_size = get_stride_size<T>;\n\n        Limiter limiter(\n            *hyperbolic_system_, limiter_parameters_, old_precomputed);\n\n        /* Skip constrained degrees of freedom: */\n        const unsigned int row_length = sparsity_simd.row_length(i);\n        if (row_length == 1)\n          return;\n\n        auto bounds =\n            bounds_.template read_tensor<T, std::array<T, n_bounds>>(i);\n\n        /*\n         * In case of a discontinuous finite element ansatz we need to\n         * extend bounds over the stencil. We do this by looping over the\n         * stencil once and taking the minimum/maximum:\n         */\n        if constexpr (have_discontinuous_ansatz) {\n          /* Skip diagonal. */\n          const unsigned int *js = sparsity_simd.columns(i) + stride_size;\n          for (unsigned int col_idx = 1; col_idx < row_length;\n               ++col_idx, js += stride_size) {\n            bounds = limiter.combine_bounds(\n                bounds,\n                bounds_.template read_tensor<T, std::array<T, n_bounds>>(js));\n          }\n          bounds_.template write_tensor<T>(bounds, i);\n        }\n\n        [[maybe_unused]] T m_i;\n        if constexpr (have_discontinuous_ansatz)\n          m_i = lumped_mass_matrix.template read_entry<T>(i);\n\n        const auto m_i_inv =\n            lumped_mass_matrix_inverse.template read_entry<T>(i);\n\n        const auto U_i_new = new_U.template read_tensor<T>(i);\n\n        const auto F_iH = r_.template read_tensor<T>(i);\n\n        const auto lambda_inv = Number(row_length - 1);\n        const auto factor = tau * m_i_inv * lambda_inv;\n\n        /* Skip diagonal. */\n        const unsigned int *js = sparsity_simd.columns(i) + stride_size;\n        for (unsigned int col_idx = 1; col_idx < row_length;\n             ++col_idx, js += stride_size) {\n\n          auto P_ij = pij_matrix_.template read_tensor<T>(i, col_idx);\n          const auto F_jH = r_.template read_tensor<T>(js);\n\n          /*\n           * Mass matrix correction:\n           */\n\n          const auto kronecker_ij = col_idx == 0 ? T(1.) : T(0.);\n\n          if constexpr (have_discontinuous_ansatz) {\n            /* Use full consistent mass matrix inverse: */\n\n            const auto m_j = lumped_mass_matrix.template read_entry<T>(js);\n            const auto m_ij_inv =\n                mass_matrix_inverse.template read_entry<T>(i, col_idx);\n            const auto b_ij = m_i * m_ij_inv - kronecker_ij;\n            const auto b_ji = m_j * m_ij_inv - kronecker_ij;\n\n            P_ij += b_ij * F_jH - b_ji * F_iH;\n\n          } else {\n            /* Use Neumann series expansion: */\n\n            const auto m_j_inv =\n                lumped_mass_matrix_inverse.template read_entry<T>(js);\n            const auto m_ij = mass_matrix.template read_entry<T>(i, col_idx);\n            const auto b_ij = kronecker_ij - m_ij * m_j_inv;\n            const auto b_ji = kronecker_ij - m_ij * m_i_inv;\n\n            P_ij += b_ij * F_jH - b_ji * F_iH;\n          }\n\n          P_ij *= factor;\n          pij_matrix_.template write_tensor<T>(P_ij, i, col_idx);\n\n          /*\n           * Compute limiter coefficients:\n           */\n\n          const auto &[l_ij, success] = limiter.limit(bounds, U_i_new, P_ij);\n          lij_matrix_.template write_entry<T>(l_ij, i, col_idx, true);\n\n          /*\n           * If the success is set to false then the low-order update\n           * resulted in a state outside of the limiter bounds. This can\n           * happen if we compute with an aggressive CFL number. We\n           * signal this condition by setting the restart_needed boolean\n           * to true and defer further action to the chosen\n           * IDViolationStrategy and the policy set in the\n           * TimeIntegrator.\n           */\n          if (!success)\n            restart_needed = true;\n        }\n      };\n\n      /*\n       * Chain through a compile time integral constant std::true_type for\n       * a discontinuous ansatz and std::false_type otherwise. We use the\n       * (constexpr) integral constant later on to avoid branching when\n       * computing d_ijH.\n       */\n      if (offline_data_->discretization().have_discontinuous_ansatz()) {\n        cpu_simd_loop<Number>(\n            loop_name(), body, 0, n_internal, n_owned, std::true_type{});\n      } else {\n        cpu_simd_loop<Number>(\n            loop_name(), body, 0, n_internal, n_owned, std::false_type{});\n      }\n\n      lij_matrix_.update_ghost_rows();\n    }\n\n    /*\n     * -------------------------------------------------------------------------\n     * Step 6, 7: Perform high-order update:\n     *\n     *   Symmetrize l_ij\n     *   High-order update: += l_ij * lambda * P_ij\n     *   Compute next l_ij\n     * -------------------------------------------------------------------------\n     */\n\n    const auto n_iterations = limiter_parameters_.iterations();\n    for (unsigned int pass = 0; pass < n_iterations; ++pass) {\n      bool last_round = (pass + 1 == n_iterations);\n\n      std::string additional_step = (last_round ? \"\" : \", next l_ij\");\n      Scope scope(\n          computing_timer_,\n          scoped_name(\"symmetrize l_ij, h.-o. update\" + additional_step));\n\n      const auto &lij_matrix =\n          (n_iterations == 2 && last_round) ? lij_matrix_next_ : lij_matrix_;\n\n      auto body = [&](auto sentinel, const unsigned int i) {\n        using T = decltype(sentinel);\n        using View =\n            typename Description::template HyperbolicSystemView<dim, T>;\n        using Limiter = typename Description::template Limiter<dim, T>;\n\n        Limiter limiter(\n            *hyperbolic_system_, limiter_parameters_, old_precomputed);\n\n        /* Skip constrained degrees of freedom: */\n        const unsigned int row_length = sparsity_simd.row_length(i);\n        if (row_length == 1)\n          return;\n\n        auto U_i_new = new_U.template read_tensor<T>(i);\n\n        const Number lambda = Number(1.) / Number(row_length - 1);\n\n        /* Stored thread locally: */\n        boost::container::small_vector<T, 54> lij_row(row_length);\n\n        /* Skip diagonal. */\n        for (unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {\n\n          const auto l_ij = std::min(\n              lij_matrix.template read_entry<T>(i, col_idx),\n              lij_matrix.template read_transposed_entry<T>(i, col_idx));\n\n          const auto p_ij = pij_matrix_.template read_tensor<T>(i, col_idx);\n\n          U_i_new += l_ij * lambda * p_ij;\n\n          if (!last_round)\n            lij_row[col_idx] = l_ij;\n        }\n\n#ifdef DEBUG_EXPENSIVE_BOUNDS_CHECK\n        const auto view = hyperbolic_system_->template view<dim, T>();\n        if (!view.is_admissible(U_i_new)) {\n          restart_needed = true;\n        }\n#endif\n\n        new_U.template write_tensor<T>(U_i_new, i);\n\n        /* Skip computating l_ij and updating p_ij in the last round */\n        if (last_round)\n          return;\n\n        const auto bounds =\n            bounds_.template read_tensor<T, std::array<T, n_bounds>>(i);\n        /* Skip diagonal. */\n        for (unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {\n\n          const auto old_l_ij = lij_row[col_idx];\n\n          const auto new_p_ij = (T(1.) - old_l_ij) *\n                                pij_matrix_.template read_tensor<T>(i, col_idx);\n\n          const auto &[new_l_ij, success] =\n              limiter.limit(bounds, U_i_new, new_p_ij);\n\n          /*\n           * This is the second pass of the limiter. Under rare\n           * circumstances the previous high-order update might be\n           * slightly out of bounds due to roundoff errors. This happens\n           * for example in flat regions or in stagnation points at a\n           * (slip boundary) point. The limiter should ensure that we do\n           * not further manipulate the state in this case. We thus only\n           * signal a restart condition if the `EXPENSIVE_BOUNDS_CHECK` debug\n           * macro is defined.\n           */\n#ifdef DEBUG_EXPENSIVE_BOUNDS_CHECK\n          if (!success)\n            restart_needed = true;\n#endif\n\n          /*\n           * Shortcut: We omit updating the p_ij and q_ij matrices and\n           * simply write (1 - l_ij^(1)) * l_ij^(2) into the l_ij matrix.\n           *\n           * This approach only works for at most two limiting steps.\n           */\n          const auto entry = (T(1.) - old_l_ij) * new_l_ij;\n          lij_matrix_next_.write_entry(entry, i, col_idx, true);\n        }\n      };\n\n      cpu_simd_loop<Number>(loop_name(), body, 0, n_internal, n_owned);\n\n      if (!last_round) {\n        lij_matrix_next_.update_ghost_rows();\n      }\n    } /* limiter_iter_ */\n\n    /*\n     * Pass through the parabolic state vector\n     */\n    const auto &old_V = std::get<2>(old_state_vector);\n    auto &new_V = std::get<2>(new_state_vector);\n    new_V = old_V;\n\n    /*\n     * Do we have to restart?\n     */\n\n    {\n      Scope scope(computing_timer_,\n                  \"time step [X] _ - synchronization barriers\");\n\n      /*\n       * Synchronize whether we have to restart the time step. Even though\n       * the restart condition itself only affects the local ensemble we\n       * nevertheless need to synchronize the boolean in case we perform\n       * synchronized global time steps. (Otherwise different ensembles\n       * might end up with a different time step.)\n       */\n      restart_needed.store(Utilities::MPI::logical_or(\n          restart_needed.load(), mpi_ensemble_.synchronization_communicator()));\n    }\n\n    if (restart_needed) {\n      switch (id_violation_strategy_) {\n      case IDViolationStrategy::warn:\n        n_warnings_++;\n#ifdef DEBUG_OUTPUT\n        std::cout << \"        raised warning, CFL/IDP violation encountered \"\n                  << std::endl;\n#endif\n        break;\n      case IDViolationStrategy::raise_exception:\n        n_restarts_++;\n        /* Suggest a restart with tau_max: */\n#ifdef DEBUG_OUTPUT\n        std::cout << \"        signalling restart (suggested_tau_max = \"\n                  << tau_max << \")\" << std::endl;\n#endif\n        throw Restart{tau_max};\n      }\n    }\n\n    /* Poison all values that are left invalid after the update step: */\n    Vectors::debug_poison_invalid_values(new_state_vector, *offline_data_);\n\n    /* Return the time step size tau: */\n    return tau;\n  }\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/initial_state_library.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"convenience_macros.h\"\n\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/tensor.h>\n\n#include <set>\n#include <string>\n\nnamespace ryujin\n{\n  /**\n   * A small abstract base class to group configuration options for a\n   * number of initial flow configurations.\n   *\n   * @note By convention all initial state configurations described by this\n   * class shall be centered at the origin (0, 0) and facing in positive x\n   * direction. The InitialValues wrapper class already allows to apply an\n   * affine translation to the coordinate system; so additional\n   * configuration options for location and direction are not needed.\n   *\n   * @ingroup InitialValues\n   */\n  template <typename Description, int dim, typename Number = double>\n  class InitialState : public dealii::ParameterAcceptor\n  {\n  public:\n    using View =\n        typename Description::template HyperbolicSystemView<dim, Number>;\n\n    using state_type = typename View::state_type;\n\n    using initial_precomputed_type = typename View::initial_precomputed_type;\n\n    /**\n     * Constructor taking initial state name @p name and a subsection @p\n     * subsection as an argument. The dealii::ParameterAcceptor is\n     * initialized with the subsubsection `subsection + \"/\" + name`.\n     */\n    InitialState(const std::string &name, const std::string &subsection)\n        : ParameterAcceptor(subsection + \"/\" + name)\n        , name_(name)\n    {\n    }\n\n    /**\n     * Given a position @p point returns the corresponding (conserved)\n     * initial state. The function is used to interpolate initial values\n     * and enforce Dirichlet boundary conditions. For the latter, the\n     * function signature has an additional parameter @p t denoting the\n     * current time to allow for time-dependent (in-flow) Dirichlet data.\n     */\n    virtual state_type compute(const dealii::Point<dim> &point, Number t) = 0;\n\n    /**\n     * Given a position @p point returns a precomputed value used for the\n     * flux computation via HyperbolicSystem::flux_contribution().\n     *\n     * The default implementation of this function simply returns a zero\n     * value. In case of the @ref ShallowWaterEquations we pre-compute the\n     * bathymetry. In case of @ref LinearTransport we precompute the\n     * advection field.\n     */\n    virtual initial_precomputed_type\n    initial_precomputations(const dealii::Point<dim> & /*point*/)\n    {\n      return initial_precomputed_type{};\n    }\n\n    /**\n     * Return the name of the initial state as (const reference) std::string\n     */\n    ACCESSOR_READ_ONLY(name)\n\n  private:\n    const std::string name_;\n  };\n\n\n  /**\n   * A \"factory\" class that is used to populate a list of all possible\n   * initial states for a given equation desribed by @p Description.\n   *\n   * This works by specializing the static member function\n   * populate_initial_state_list for all possible equation @p Description.\n   *\n   * @ingroup InitialValues\n   */\n  template <typename Description, int dim, typename Number>\n  class InitialStateLibrary\n  {\n  public:\n    /**\n     * @copydoc HyperbolicSystem\n     */\n    using HyperbolicSystem = typename Description::HyperbolicSystem;\n\n    /**\n     * @copydoc HyperbolicSystem\n     */\n    using ParabolicSystem = typename Description::ParabolicSystem;\n\n    /**\n     * The type of the initial state list\n     */\n    using initial_state_list_type =\n        std::set<std::unique_ptr<InitialState<Description, dim, Number>>>;\n\n    /**\n     * Populate a given container with all initial states defined for the\n     * given equation @p Description and dimension @p dim.\n     *\n     * @ingroup InitialValues\n     */\n    static void\n    populate_initial_state_list(initial_state_list_type &initial_state_list,\n                                const HyperbolicSystem &h,\n                                const ParabolicSystem &p,\n                                const std::string &s);\n  };\n} // namespace ryujin\n"
  },
  {
    "path": "source/initial_values.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2023 by the ryujin authors\n//\n\n#include \"initial_values.template.h\"\n#include <instantiate.h>\n\nnamespace ryujin\n{\n  /* instantiations */\n  template class InitialValues<Description, 1, NUMBER>;\n  template class InitialValues<Description, 2, NUMBER>;\n  template class InitialValues<Description, 3, NUMBER>;\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/initial_values.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"initial_state_library.h\"\n#include \"mpi_ensemble.h\"\n#include \"observer_pointer.h\"\n#include \"offline_data.h\"\n\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/tensor.h>\n\n#include <functional>\n\nnamespace ryujin\n{\n  /**\n   * A class implementing a number of different initial value\n   * configurations.\n   *\n   * Given a position @p point the member function\n   * InitialValues::initial_state() returns the corresponding (conserved)\n   * initial state. The function is used to interpolate initial values and\n   * enforce Dirichlet boundary conditions. For the latter, the the\n   * function signature has an additional parameter @p t denoting the\n   * current time to allow for time-dependent (in-flow) Dirichlet data.\n   *\n   * For validation purposes a number of analytic solutions are implemented\n   * as well.\n   *\n   * @ingroup InitialValues\n   */\n  template <typename Description, int dim, typename Number = double>\n  class InitialValues : public dealii::ParameterAcceptor\n  {\n  public:\n    /**\n     * @name Typedefs and constexpr constants\n     */\n    //@{\n    using HyperbolicSystem = typename Description::HyperbolicSystem;\n\n    using ParabolicSystem = typename Description::ParabolicSystem;\n\n    using View =\n        typename Description::template HyperbolicSystemView<dim, Number>;\n\n    static constexpr auto problem_dimension = View::problem_dimension;\n\n    using state_type = typename View::state_type;\n\n    static constexpr auto n_initial_precomputed_values =\n        View::n_initial_precomputed_values;\n\n    using initial_precomputed_type = typename View::initial_precomputed_type;\n\n    using HyperbolicVector = typename View::HyperbolicVector;\n\n    using InitialPrecomputedVector = typename View::InitialPrecomputedVector;\n\n    //@}\n    /**\n     * @name Interpolate initial states\n     */\n    //@{\n\n    /**\n     * Constructor.\n     */\n    InitialValues(const MPIEnsemble &mpi_ensemble,\n                  const OfflineData<dim, Number> &offline_data,\n                  const HyperbolicSystem &hyperbolic_system,\n                  const ParabolicSystem &parabolic_system,\n                  const std::string &subsection = \"/InitialValues\");\n\n\n    /**\n     * Callback for ParameterAcceptor::initialize(). After we read in\n     * configuration parameters from the parameter file we have to do some\n     * (minor) preparatory work in this class to precompute some initial\n     * state values. Do this with a callback.\n     */\n    void parse_parameters_callback();\n\n\n    /**\n     * Given a position @p point returns the corresponding (conserved)\n     * initial state. The function is used to interpolate initial values\n     * and enforce Dirichlet boundary conditions. For the latter, the the\n     * function signature has an additional parameter @p t denoting the\n     * current time to allow for time-dependent (in-flow) Dirichlet data.\n     */\n    DEAL_II_ALWAYS_INLINE inline state_type\n    initial_state(const dealii::Point<dim> &point, Number t) const\n    {\n      return initial_state_(point, t);\n    }\n\n\n    /**\n     * Given a position @p point returns the corresponding (conserved)\n     * initial state. The function is used to interpolate initial values\n     * and enforce Dirichlet boundary conditions. For the latter, the the\n     * function signature has an additional parameter @p t denoting the\n     * current time to allow for time-dependent (in-flow) Dirichlet data.\n     */\n    DEAL_II_ALWAYS_INLINE inline initial_precomputed_type\n    initial_precomputed(const dealii::Point<dim> &point) const\n    {\n      return initial_precomputed_(point);\n    }\n\n\n    /**\n     * This routine computes and returns a state vector populated with\n     * initial values for a specified time @p t.\n     */\n    HyperbolicVector interpolate_hyperbolic_vector(Number t = 0) const;\n\n\n    /**\n     * This routine computes and returns a state vector populated with\n     * initial values for a specified time @p t.\n     */\n    InitialPrecomputedVector interpolate_initial_precomputed_vector() const;\n\n  private:\n    //@}\n    /**\n     * @name Run time options\n     */\n    //@{\n\n    std::string configuration_;\n\n    dealii::Point<dim> initial_position_;\n\n    dealii::Tensor<1, dim> initial_direction_;\n\n    Number perturbation_;\n\n    //@}\n    /**\n     * @name Internal data:\n     */\n    //@{\n\n    const MPIEnsemble &mpi_ensemble_;\n\n    dealii::ObserverPointer<const OfflineData<dim, Number>> offline_data_;\n    dealii::ObserverPointer<const HyperbolicSystem> hyperbolic_system_;\n    dealii::ObserverPointer<const ParabolicSystem> parabolic_system_;\n\n    typename InitialStateLibrary<Description, dim, Number>::\n        initial_state_list_type initial_state_list_;\n\n    std::function<state_type(const dealii::Point<dim> &, Number)>\n        initial_state_;\n\n    std::function<initial_precomputed_type(const dealii::Point<dim> &)>\n        initial_precomputed_;\n\n    //@}\n  };\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/initial_values.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include \"initial_values.h\"\n\n#include <deal.II/numerics/vector_tools.h>\n#include <deal.II/numerics/vector_tools.templates.h>\n\n#include <random>\n\nnamespace ryujin\n{\n  using namespace dealii;\n\n  template <typename Description, int dim, typename Number>\n  InitialValues<Description, dim, Number>::InitialValues(\n      const MPIEnsemble &mpi_ensemble,\n      const OfflineData<dim, Number> &offline_data,\n      const HyperbolicSystem &hyperbolic_system,\n      const ParabolicSystem &parabolic_system,\n      const std::string &subsection)\n      : ParameterAcceptor(subsection)\n      , mpi_ensemble_(mpi_ensemble)\n      , offline_data_(&offline_data)\n      , hyperbolic_system_(&hyperbolic_system)\n      , parabolic_system_(&parabolic_system)\n  {\n    ParameterAcceptor::parse_parameters_call_back.connect(std::bind(\n        &InitialValues<Description, dim, Number>::parse_parameters_callback,\n        this));\n\n    configuration_ = \"uniform\";\n    add_parameter(\"configuration\",\n                  configuration_,\n                  \"The initial state configuration. Valid names are given by \"\n                  \"any of the subsections defined below.\");\n\n    initial_direction_[0] = 1.;\n    add_parameter(\n        \"direction\",\n        initial_direction_,\n        \"Initial direction of initial configuration (Galilei transform)\");\n\n    initial_position_[0] = 1.;\n    add_parameter(\n        \"position\",\n        initial_position_,\n        \"Initial position of initial configuration (Galilei transform)\");\n\n    perturbation_ = 0.;\n    add_parameter(\"perturbation\",\n                  perturbation_,\n                  \"Add a random perturbation of the specified magnitude to the \"\n                  \"initial state.\");\n\n    /*\n     * And finally populate the initial state list with all initial state\n     * configurations defined in the InitialStateLibrary namespace:\n     */\n    InitialStateLibrary<Description, dim, Number>::populate_initial_state_list(\n        initial_state_list_,\n        *hyperbolic_system_,\n        *parabolic_system_,\n        subsection);\n  }\n\n  namespace\n  {\n    /**\n     * An affine transformation:\n     */\n    template <int dim>\n    inline DEAL_II_ALWAYS_INLINE dealii::Point<dim>\n    affine_transform(const dealii::Tensor<1, dim> initial_direction,\n                     const dealii::Point<dim> initial_position,\n                     const dealii::Point<dim> x)\n    {\n      auto direction = x - initial_position;\n\n      /* Roll third component of initial_direction onto xy-plane: */\n      if constexpr (dim == 3) {\n        auto n_x = initial_direction[0];\n        auto n_z = initial_direction[2];\n        const auto norm = std::sqrt(n_x * n_x + n_z * n_z);\n        n_x /= norm;\n        n_z /= norm;\n        auto new_direction = direction;\n        if (norm > 1.0e-14) {\n          new_direction[0] = n_x * direction[0] + n_z * direction[2];\n          new_direction[2] = -n_z * direction[0] + n_x * direction[2];\n        }\n        direction = new_direction;\n      }\n\n      /* Roll second component of initial_direction onto x-axis: */\n      if constexpr (dim >= 2) {\n        auto n_x = initial_direction[0];\n        auto n_y = initial_direction[1];\n        const auto norm = std::sqrt(n_x * n_x + n_y * n_y);\n        n_x /= norm;\n        n_y /= norm;\n        auto new_direction = direction;\n        if (norm > 1.0e-14) {\n          new_direction[0] = n_x * direction[0] + n_y * direction[1];\n          new_direction[1] = -n_y * direction[0] + n_x * direction[1];\n        }\n        direction = new_direction;\n      }\n\n      if constexpr (dim == 1) {\n        auto n = initial_direction[0];\n        const auto norm = std::abs(n);\n        n /= norm;\n        auto new_direction = direction;\n        if (norm > 1.0e-14)\n          new_direction[0] = n * direction[0];\n        direction = new_direction;\n      }\n\n      return Point<dim>() + direction;\n    }\n\n\n    /**\n     * Transform vector:\n     */\n    template <int dim, typename Number>\n    inline DEAL_II_ALWAYS_INLINE dealii::Tensor<1, dim, Number>\n    affine_transform_vector(const dealii::Tensor<1, dim> initial_direction,\n                            dealii::Tensor<1, dim, Number> direction)\n    {\n      if constexpr (dim == 1) {\n        auto n = initial_direction[0];\n        const auto norm = std::abs(n);\n        n /= norm;\n        auto new_direction = direction;\n        if (norm > 1.0e-14)\n          new_direction[0] = n * direction[0];\n        direction = new_direction;\n      }\n\n      if constexpr (dim >= 2) {\n        auto n_x = initial_direction[0];\n        auto n_y = initial_direction[1];\n        const auto norm = std::sqrt(n_x * n_x + n_y * n_y);\n        n_x /= norm;\n        n_y /= norm;\n        auto new_direction = direction;\n        if (norm > 1.0e-14) {\n          new_direction[0] = n_x * direction[0] - n_y * direction[1];\n          new_direction[1] = n_y * direction[0] + n_x * direction[1];\n        }\n        direction = new_direction;\n      }\n\n      if constexpr (dim == 3) {\n        auto n_x = initial_direction[0];\n        auto n_z = initial_direction[2];\n        const auto norm = std::sqrt(n_x * n_x + n_z * n_z);\n        n_x /= norm;\n        n_z /= norm;\n        auto new_direction = direction;\n        if (norm > 1.0e-14) {\n          new_direction[0] = n_x * direction[0] - n_z * direction[2];\n          new_direction[2] = n_z * direction[0] + n_x * direction[2];\n        }\n        direction = new_direction;\n      }\n\n      return direction;\n    }\n  } /* namespace */\n\n\n  template <typename Description, int dim, typename Number>\n  void InitialValues<Description, dim, Number>::parse_parameters_callback()\n  {\n    /* First, let's normalize the direction: */\n\n    AssertThrow(initial_direction_.norm() != 0.,\n                ExcMessage(\"Initial direction is set to the zero vector.\"));\n    initial_direction_ /= initial_direction_.norm();\n\n    /* Populate std::function object: */\n\n    {\n      bool initialized = false;\n      for (auto &it : initial_state_list_)\n        if (it->name() == configuration_) {\n          initial_state_ = [this, &it](const dealii::Point<dim> &point,\n                                       Number t) {\n            const auto transformed_point =\n                affine_transform(initial_direction_, initial_position_, point);\n            auto state = it->compute(transformed_point, t);\n            const auto view = hyperbolic_system_->template view<dim, Number>();\n            state =\n                view.apply_galilei_transform(state, [&](const auto &momentum) {\n                  return affine_transform_vector(initial_direction_, momentum);\n                });\n            return state;\n          };\n\n          initial_precomputed_ = [this, &it](const dealii::Point<dim> &point) {\n            const auto transformed_point =\n                affine_transform(initial_direction_, initial_position_, point);\n            return it->initial_precomputations(transformed_point);\n          };\n\n          initialized = true;\n          break;\n        }\n\n      AssertThrow(\n          initialized,\n          ExcMessage(\n              \"Could not find an initial state description with name \\\"\" +\n              configuration_ + \"\\\"\"));\n    }\n\n    /* Add a random perturbation to the original function object: */\n\n    if (perturbation_ != 0.) {\n      initial_state_ = [old_state = this->initial_state_,\n                        perturbation = this->perturbation_](\n                           const dealii::Point<dim> &point, Number t) {\n        auto state = old_state(point, t);\n\n        if (t > 0.)\n          return state;\n\n        static std::default_random_engine generator =\n            std::default_random_engine(std::random_device()());\n        static std::uniform_real_distribution<Number> distribution(-1., 1.);\n        static auto draw = std::bind(distribution, generator);\n        for (unsigned int i = 0; i < problem_dimension; ++i)\n          state[i] *= (Number(1.) + perturbation * draw());\n\n        return state;\n      };\n    }\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  auto InitialValues<Description, dim, Number>::interpolate_hyperbolic_vector(\n      Number t) const -> HyperbolicVector\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"InitialValues<dim, Number>::\"\n              << \"interpolate_hyperbolic_vector(t = \" << t << \")\" << std::endl;\n#endif\n\n    const auto &scalar_partitioner = offline_data_->scalar_partitioner();\n    const auto &vector_partitioner =\n        offline_data_->hyperbolic_vector_partitioner();\n\n    HyperbolicVector U;\n    U.reinit_with_vector_partitioner(vector_partitioner);\n\n    using ScalarHostVector = Vectors::ScalarHostVector<Number>;\n    ScalarHostVector temp;\n    temp.reinit(scalar_partitioner);\n\n    // FIXME: it is not particularly efficient to call\n    // VectorTools::interpolate for every component separately. If this\n    // gets too slow, we should consider writing out to a temporary (block)\n    // vector and then inserting into the MultiComponentVector.\n    const auto callable = [&](const auto &p) { return initial_state(p, t); };\n    for (unsigned int d = 0; d < problem_dimension; ++d) {\n      VectorTools::interpolate(offline_data_->discretization().mapping(),\n                               offline_data_->dof_handler(),\n                               to_function<dim, Number>(callable, d),\n                               temp);\n      U.insert_component(temp, d);\n    }\n\n    U.update_ghost_values();\n\n    return U;\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  auto InitialValues<Description, dim, Number>::\n      interpolate_initial_precomputed_vector() const -> InitialPrecomputedVector\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"InitialValues<dim, Number>::\"\n              << \"interpolate_initial_precomputed_vector()\" << std::endl;\n#endif\n\n    const auto &scalar_partitioner = offline_data_->scalar_partitioner();\n\n    InitialPrecomputedVector precomputed;\n    precomputed.reinit_with_scalar_partitioner(scalar_partitioner);\n\n    if constexpr (n_initial_precomputed_values == 0)\n      return precomputed;\n\n    using ScalarHostVector = Vectors::ScalarHostVector<Number>;\n    ScalarHostVector temp;\n    temp.reinit(scalar_partitioner);\n\n    // FIXME: it is not particularly efficient to call\n    // VectorTools::interpolate for every component separately. If this\n    // gets too slow, we should consider writing out to a temporary (block)\n    // vector and then inserting into the MultiComponentVector.\n    const auto callable = [&](const auto &p) { return initial_precomputed(p); };\n    for (unsigned int d = 0; d < n_initial_precomputed_values; ++d) {\n      VectorTools::interpolate(offline_data_->dof_handler(),\n                               to_function<dim, Number>(callable, d),\n                               temp);\n      precomputed.insert_component(temp, d);\n    }\n\n    precomputed.update_ghost_values();\n    return precomputed;\n  }\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/instrumentation.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n/**\n * @name Likwid support\n */\n//@{\n\n#ifdef WITH_LIKWID\n#include <likwid.h>\n#else\n\n/**\n * Macro initializing likwid instrumentation. Used in main().\n */\n#define LIKWID_MARKER_INIT\n\n\n/**\n * Macro initializing likwid instrumentation on a worker thread. Used in main().\n */\n#define LIKWID_MARKER_THREAD_INIT\n\n/**\n * Macro finalizing likwid instrumentation. Used in main().\n */\n#define LIKWID_MARKER_CLOSE\n\n/**\n * A set of macros that start and stop likwid instrumentation (if support\n * for likwid is enabled). We currently wrap the hot paths in the\n * Euler and Navier-Stokes modules in the HyperbolicModule::step() and\n * DissipationModule::step() functions. Usage:\n *\n * @code\n * LIKWID_MARKER_START(\"string identifier\")\n * // critical compute kernel section\n * LIKWID_MARKER_STOP(\"string identifier\")\n * @endcode\n */\n#define LIKWID_MARKER_START(opt)\n\n/**\n * @copydoc LIKWID_MARKER_START\n */\n#define LIKWID_MARKER_STOP(opt)\n\n#endif\n\n//@}\n/**\n * @name Clang LSAN support\n */\n//@{\n\n/**\n * Explicitly disable/enable the LLVM/Clang LeakSanitiver\n *\n * @code\n * LSAN_DISABLE\n * // Calling some external code path that is leaky and that we cannot\n * // control...\n * LSAN_ENABLE\n * @endcode\n */\n#define LSAN_DISABLE\n\n/**\n * @copydoc LSAN_DISABLE\n */\n#define LSAN_ENABLE\n\n#if defined(__clang__) && defined(DEBUG)\n#if __has_feature(address_sanitizer)\n#include <sanitizer/lsan_interface.h>\n#undef LSAN_DISABLE\n#define LSAN_DISABLE __lsan_disable();\n#undef LSAN_ENABLE\n#define LSAN_ENABLE __lsan_enable();\n#endif\n#endif\n\n//@}\n"
  },
  {
    "path": "source/lazy.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2024 by Matthias Maier\n// Copyright (C) 2024 - 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <deal.II/base/exceptions.h>\n#include <deal.II/base/memory_consumption.h>\n#include <deal.II/base/mutex.h>\n\n#include <atomic>\n#include <mutex>\n#include <optional>\n\n\nnamespace ryujin\n{\n  /**\n   * This is a slightly minimized variant of the Lazy<T> initialization class\n   * shipped with the current development version of deal.II.\n   */\n  template <typename T>\n  class Lazy\n  {\n  public:\n    Lazy();\n    Lazy(const Lazy &other);\n    Lazy(Lazy &&other) noexcept;\n\n    Lazy &operator=(const Lazy &other);\n    Lazy &operator=(Lazy &&other) noexcept;\n\n    void reset() noexcept;\n\n    template <typename Callable>\n    void ensure_initialized(const Callable &creator) const;\n\n    bool has_value() const;\n\n    const T &value() const;\n    T &value();\n\n  private:\n    mutable std::optional<T> object;\n    mutable std::atomic<bool> object_is_initialized;\n    mutable dealii::Threads::Mutex initialization_mutex;\n  };\n\n\n  // ------------------------------- inline functions --------------------------\n\n\n  template <typename T>\n  inline Lazy<T>::Lazy()\n      : object_is_initialized(false)\n  {\n  }\n\n\n  template <typename T>\n  inline Lazy<T>::Lazy(const Lazy &other)\n      : object(other.object)\n  {\n    object_is_initialized.store(other.object_is_initialized.load());\n  }\n\n\n  template <typename T>\n  inline Lazy<T>::Lazy(Lazy &&other) noexcept\n      : object(std::move(other.object))\n  {\n    object_is_initialized.store(other.object_is_initialized.load());\n\n    other.object_is_initialized.store(false);\n    other.object.reset();\n  }\n\n\n  template <typename T>\n  inline Lazy<T> &Lazy<T>::operator=(const Lazy &other)\n  {\n    object = other.object;\n    object_is_initialized.store(other.object_is_initialized.load());\n\n    return *this;\n  }\n\n\n  template <typename T>\n  inline Lazy<T> &Lazy<T>::operator=(Lazy &&other) noexcept\n  {\n    object = std::move(other.object);\n    object_is_initialized.store(other.object_is_initialized.load());\n\n    other.object_is_initialized.store(false);\n    other.object.reset();\n\n    return *this;\n  }\n\n\n  template <typename T>\n  inline void Lazy<T>::reset() noexcept\n  {\n    object_is_initialized.store(false);\n    object.reset();\n  }\n\n\n  template <typename T>\n  template <typename Callable>\n  inline DEAL_II_ALWAYS_INLINE void\n  Lazy<T>::ensure_initialized(const Callable &creator) const\n  {\n    // Check the object_is_initialized atomic with \"acquire\" semantics.\n    if (!object_is_initialized.load(std::memory_order_acquire))\n#ifdef DEAL_II_HAVE_CXX20\n        [[unlikely]]\n#endif\n    {\n      std::lock_guard<std::mutex> lock(initialization_mutex);\n\n      //\n      // Check again. If this thread won the race to the lock then we\n      // initialize the object. Otherwise another thread has already\n      // initialized the object and flipped the object_is_initialized\n      // bit. (Here, the initialization_mutex ensures consistent ordering\n      // with a memory fence, so we will observe the updated bool without\n      // acquire semantics.)\n      //\n      if (!object_is_initialized.load(std::memory_order_relaxed)) {\n        Assert(object.has_value() == false, dealii::ExcInternalError());\n        object.emplace(std::move(creator()));\n\n        // Flip the object_is_initialized boolean with \"release\" semantics.\n        object_is_initialized.store(true, std::memory_order_release);\n      }\n    }\n  }\n\n\n  template <typename T>\n  inline DEAL_II_ALWAYS_INLINE bool Lazy<T>::has_value() const\n  {\n    return (object_is_initialized && object.has_value());\n  }\n\n\n  template <typename T>\n  inline DEAL_II_ALWAYS_INLINE const T &Lazy<T>::value() const\n  {\n    Assert(object_is_initialized && object.has_value(),\n           dealii::ExcMessage(\n               \"value() has been called but the contained object has not been \"\n               \"initialized. Did you forget to call 'ensure_initialized()' \"\n               \"first?\"));\n\n    return object.value();\n  }\n\n\n  template <typename T>\n  inline DEAL_II_ALWAYS_INLINE T &Lazy<T>::value()\n  {\n    Assert(object_is_initialized && object.has_value(),\n           dealii::ExcMessage(\n               \"value() has been called but the contained object has not been \"\n               \"initialized. Did you forget to call 'ensure_initialized()' \"\n               \"first?\"));\n\n    return object.value();\n  }\n\n} // namespace ryujin\n"
  },
  {
    "path": "source/local_index_handling.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <deal.II/base/partitioner.h>\n#include <deal.II/dofs/dof_handler.h>\n#include <deal.II/dofs/dof_renumbering.h>\n#include <deal.II/dofs/dof_tools.h>\n#include <deal.II/lac/affine_constraints.h>\n#include <deal.II/lac/dynamic_sparsity_pattern.h>\n#include <deal.II/lac/sparsity_tools.h>\n\nnamespace ryujin\n{\n  /**\n   * Given a @p partitioner the function converts an AffineConstraints\n   * object @p affine_constraints from the usual global numbering used in\n   * deal.II to an MPI-rank local numbering \\f$[0,n]\\f$, where \\f$n\\f$ is\n   * the number of locally relevant degrees of freedom.\n   *\n   * @ingroup FiniteElement\n   */\n  template <typename Number>\n  void transform_to_local_range(\n      const dealii::Utilities::MPI::Partitioner &partitioner,\n      dealii::AffineConstraints<Number> &affine_constraints)\n  {\n    affine_constraints.close();\n\n    dealii::AffineConstraints<Number> temporary;\n\n    for (auto line : affine_constraints.get_lines()) {\n      /* translate into local index ranges: */\n      line.index = partitioner.global_to_local(line.index);\n      std::transform(line.entries.begin(),\n                     line.entries.end(),\n                     line.entries.begin(),\n                     [&](auto entry) {\n                       return std::make_pair(\n                           partitioner.global_to_local(entry.first),\n                           entry.second);\n                     });\n\n      temporary.add_line(line.index);\n      temporary.add_entries(line.index, line.entries);\n      temporary.set_inhomogeneity(line.index, line.inhomogeneity);\n    }\n\n    temporary.close();\n\n    affine_constraints = std::move(temporary);\n  }\n\n\n  /**\n   * Given a @p partitioner the function translates each element of a given\n   * vector @p vector from the usual global numbering used in deal.II to an\n   * MPI-rank local numbering \\f$[0,n]\\f$, where \\f$n\\f$ is the number of\n   * locally relevant degrees of freedom.\n   *\n   * @ingroup FiniteElement\n   */\n  template <typename VECTOR>\n  void transform_to_local_range(\n      const dealii::Utilities::MPI::Partitioner &partitioner, VECTOR &vector)\n  {\n    std::transform(\n        vector.begin(), vector.end(), vector.begin(), [&](auto index) {\n          return partitioner.global_to_local(index);\n        });\n  }\n\n\n  /**\n   * The DoFRenumbering namespace contains a number of custom dof\n   * renumbering functions.\n   *\n   * @ingroup FiniteElement\n   */\n  namespace DoFRenumbering\n  {\n    /**\n     * Import the Cuthill McKee reordering from deal.II into the current\n     * namespace.\n     */\n    using dealii::DoFRenumbering::Cuthill_McKee;\n\n    /**\n     * Reorder all (strides of) locally internal indices that contain\n     * export indices to the start of the index range.\n     *\n     * This renumbering requires MPI communication in order to determine\n     * the set of export indices.\n     *\n     * @ingroup FiniteElement\n     */\n    template <int dim>\n    unsigned int export_indices_first(dealii::DoFHandler<dim> &dof_handler,\n                                      const MPI_Comm &mpi_communicator,\n                                      const unsigned int n_locally_internal,\n                                      const std::size_t group_size)\n    {\n      using namespace dealii;\n\n      const IndexSet &locally_owned = dof_handler.locally_owned_dofs();\n      const auto n_locally_owned = locally_owned.n_elements();\n\n      /* The locally owned index range has to be contiguous */\n      Assert(locally_owned.is_contiguous() == true,\n             dealii::ExcMessage(\n                 \"Need a contiguous set of locally owned indices.\"));\n\n      /* Offset to translate from global to local index range */\n      const auto offset = n_locally_owned != 0 ? *locally_owned.begin() : 0;\n\n#if DEAL_II_VERSION_GTE(9, 6, 0)\n      const auto locally_relevant =\n          DoFTools::extract_locally_relevant_dofs(dof_handler);\n#else\n      IndexSet locally_relevant;\n      DoFTools::extract_locally_relevant_dofs(dof_handler, locally_relevant);\n#endif\n\n      /* Create a temporary MPI partitioner: */\n\n      Utilities::MPI::Partitioner partitioner(\n          locally_owned, locally_relevant, mpi_communicator);\n\n      IndexSet export_indices(n_locally_owned);\n      for (const auto &it : partitioner.import_indices()) {\n        export_indices.add_range(it.first, it.second);\n      }\n\n      std::vector<dealii::types::global_dof_index> new_order(n_locally_owned);\n\n      /*\n       * First pass: reorder all strides containing export indices and mark\n       * all other indices with numbers::invalid_dof_index:\n       */\n\n      unsigned int n_export_indices = 0;\n\n      Assert(n_locally_internal <= n_locally_owned, dealii::ExcInternalError());\n\n      for (unsigned int i = 0; i < n_locally_internal; i += group_size) {\n        bool export_index_present = false;\n        for (unsigned int j = 0; j < group_size; ++j) {\n          if (export_indices.is_element(i + j)) {\n            export_index_present = true;\n            break;\n          }\n        }\n\n        if (export_index_present) {\n          Assert(n_export_indices % group_size == 0,\n                 dealii::ExcInternalError());\n          for (unsigned int j = 0; j < group_size; ++j) {\n            new_order[i + j] = offset + n_export_indices++;\n          }\n        } else {\n          for (unsigned int j = 0; j < group_size; ++j)\n            new_order[i + j] = dealii::numbers::invalid_dof_index;\n        }\n      }\n\n#if DEBUG\n      unsigned int n_other = 0;\n      for (unsigned int i = n_locally_internal; i < n_locally_owned; ++i)\n        if (export_indices.is_element(i))\n          n_other++;\n\n      Assert(n_other + n_export_indices >= export_indices.n_elements(),\n             dealii::ExcInternalError());\n#endif\n\n      unsigned int running_index = n_export_indices;\n\n      /*\n       * Second pass: append the rest:\n       */\n\n      for (unsigned int i = 0; i < n_locally_internal; i += group_size) {\n        if (new_order[i] == dealii::numbers::invalid_dof_index) {\n          for (unsigned int j = 0; j < group_size; ++j) {\n            Assert(new_order[i + j] == dealii::numbers::invalid_dof_index,\n                   dealii::ExcInternalError());\n            new_order[i + j] = offset + running_index++;\n          }\n        }\n      }\n\n      Assert(running_index == n_locally_internal, dealii::ExcInternalError());\n\n      for (unsigned int i = n_locally_internal; i < n_locally_owned; i++) {\n        new_order[i] = offset + running_index++;\n      }\n\n      Assert(running_index == n_locally_owned, dealii::ExcInternalError());\n\n      dof_handler.renumber_dofs(new_order);\n\n      Assert(n_export_indices % group_size == 0, dealii::ExcInternalError());\n      Assert(n_export_indices <= n_locally_internal,\n             dealii::ExcInternalError());\n      return n_export_indices;\n    }\n\n\n    /**\n     * Reorder all strides of inconsistent locally internal indices to the\n     * end of the locally internal index range.\n     *\n     * @ingroup FiniteElement\n     */\n    template <int dim>\n    unsigned int\n    inconsistent_strides_last(dealii::DoFHandler<dim> &dof_handler,\n                              const dealii::DynamicSparsityPattern &sparsity,\n                              const unsigned int n_locally_internal,\n                              const std::size_t group_size)\n    {\n      using namespace dealii;\n\n      const IndexSet &locally_owned = dof_handler.locally_owned_dofs();\n      const auto n_locally_owned = locally_owned.n_elements();\n\n      /* The locally owned index range has to be contiguous */\n      Assert(locally_owned.is_contiguous() == true,\n             dealii::ExcMessage(\n                 \"Need a contiguous set of locally owned indices.\"));\n\n      /* Offset to translate from global to local index range */\n      const auto offset = n_locally_owned != 0 ? *locally_owned.begin() : 0;\n\n      std::vector<dealii::types::global_dof_index> new_order(n_locally_owned);\n\n      /*\n       * First pass: keep all strides with consistent row length at the\n       * beginning of the locally internal index range and mark all other\n       * indices with numbers::invalid_dof_index:\n       */\n\n      unsigned int n_consistent_range = 0;\n\n      Assert(n_locally_internal <= n_locally_owned, dealii::ExcInternalError());\n\n      for (unsigned int i = 0; i < n_locally_internal; i += group_size) {\n\n        bool stride_is_consistent = true;\n        const auto group_row_length = sparsity.row_length(offset + i);\n        for (unsigned int j = 0; j < group_size; ++j) {\n          if (group_row_length != sparsity.row_length(offset + i + j)) {\n            stride_is_consistent = false;\n            break;\n          }\n        }\n\n        if (stride_is_consistent) {\n          for (unsigned int j = 0; j < group_size; ++j) {\n            new_order[i + j] = offset + n_consistent_range++;\n          }\n        } else {\n          for (unsigned int j = 0; j < group_size; ++j)\n            new_order[i + j] = dealii::numbers::invalid_dof_index;\n        }\n      }\n\n      /*\n       * Second pass: append the rest:\n       */\n\n      unsigned int running_index = n_consistent_range;\n\n      for (unsigned int i = 0; i < n_locally_internal; i += group_size) {\n        if (new_order[i] == dealii::numbers::invalid_dof_index) {\n          for (unsigned int j = 0; j < group_size; ++j) {\n            Assert(new_order[i + j] == dealii::numbers::invalid_dof_index,\n                   dealii::ExcInternalError());\n            new_order[i + j] = offset + running_index++;\n          }\n        }\n      }\n\n      Assert(running_index == n_locally_internal, dealii::ExcInternalError());\n\n      for (unsigned int i = n_locally_internal; i < n_locally_owned; i++) {\n        new_order[i] = offset + running_index++;\n      }\n\n      Assert(running_index == n_locally_owned, dealii::ExcInternalError());\n\n      dof_handler.renumber_dofs(new_order);\n\n      Assert(n_consistent_range % group_size == 0, dealii::ExcInternalError());\n      Assert(n_consistent_range <= n_locally_internal,\n             dealii::ExcInternalError());\n      return n_consistent_range;\n    }\n\n\n    /**\n     * Reorder indices:\n     *\n     * In order to traverse over multiple rows of a (to be constructed)\n     * sparsity pattern simultaneously using SIMD instructions we reorder\n     * all locally owned degrees of freedom to ensure that a local index\n     * range \\f$[0, \\text{n_locally_internal_}) \\subset [0,\n     * \\text{n_locally_owned})\\f$ is available that groups dofs with same\n     * stencil size in groups of multiples of @p group_size\n     *\n     * Returns the right boundary n_internal of the internal index range.\n     *\n     * @ingroup FiniteElement\n     */\n    template <int dim>\n    unsigned int internal_range(dealii::DoFHandler<dim> &dof_handler,\n                                const dealii::DynamicSparsityPattern &sparsity,\n                                const std::size_t group_size)\n    {\n      using namespace dealii;\n\n      const auto &locally_owned = dof_handler.locally_owned_dofs();\n      const auto n_locally_owned = locally_owned.n_elements();\n\n      /* The locally owned index range has to be contiguous */\n\n      Assert(locally_owned.is_contiguous() == true,\n             dealii::ExcMessage(\n                 \"Need a contiguous set of locally owned indices.\"));\n\n      /* Offset to translate from global to local index range */\n      const auto offset = n_locally_owned != 0 ? *locally_owned.begin() : 0;\n\n      using dof_type = dealii::types::global_dof_index;\n      std::vector<dof_type> new_order(n_locally_owned);\n      dof_type current_index = offset;\n\n      /*\n       * Sort degrees of freedom into a map grouped by stencil size. Write\n       * out dof indices into the new_order vector in groups of group_size\n       * and with same stencil size.\n       */\n\n      std::map<unsigned int, std::set<dof_type>> bins;\n\n      for (unsigned int i = 0; i < n_locally_owned; ++i) {\n        const dof_type index = i;\n        const unsigned int row_length = sparsity.row_length(offset + index);\n        bins[row_length].insert(index);\n\n        if (bins[row_length].size() == group_size) {\n          for (const auto &index : bins[row_length])\n            new_order[index] = current_index++;\n          bins.erase(row_length);\n        }\n      }\n\n      unsigned int n_locally_internal = current_index - offset;\n\n      /* Write out the rest. */\n\n      for (const auto &entries : bins) {\n        Assert(entries.second.size() > 0, ExcInternalError());\n        for (const auto &index : entries.second)\n          new_order[index] = current_index++;\n      }\n      Assert(current_index == offset + n_locally_owned, ExcInternalError());\n\n      dof_handler.renumber_dofs(new_order);\n\n      Assert(n_locally_internal % group_size == 0, ExcInternalError());\n      return n_locally_internal;\n    }\n  } // namespace DoFRenumbering\n\n\n  /**\n   * The DoFTools namespace contains a number of custom dof tools\n   * functions.\n   *\n   * @ingroup FiniteElement\n   */\n  namespace DoFTools\n  {\n    /** Import a function from deal.II into the current namespace. */\n    using dealii::DoFTools::extract_locally_relevant_dofs;\n\n    /** Import a function from deal.II into the current namespace. */\n    using dealii::DoFTools::make_hanging_node_constraints;\n\n    /** Import a function from deal.II into the current namespace. */\n    using dealii::DoFTools::make_periodicity_constraints;\n\n    /** Import a function from deal.II into the current namespace. */\n    using dealii::DoFTools::make_sparsity_pattern;\n\n\n    /**\n     * Given a @p dof_handler, and constraints @p affine_constraints this\n     * function creates an extended sparsity pattern that also includes\n     * locally relevant to locally relevant couplings.\n     *\n     * @ingroup FiniteElement\n     */\n    template <int dim, typename Number, typename SPARSITY>\n    void make_extended_sparsity_pattern(\n        const dealii::DoFHandler<dim> &dof_handler,\n        SPARSITY &dsp,\n        const dealii::AffineConstraints<Number> &affine_constraints,\n        bool keep_constrained)\n    {\n      std::vector<dealii::types::global_dof_index> dof_indices;\n\n      for (auto cell : dof_handler.active_cell_iterators()) {\n        /* iterate over locally owned cells and the ghost layer */\n        if (cell->is_artificial())\n          continue;\n\n        const unsigned int dofs_per_cell = cell->get_fe().n_dofs_per_cell();\n        dof_indices.resize(dofs_per_cell);\n        cell->get_dof_indices(dof_indices);\n\n        affine_constraints.add_entries_local_to_global(\n            dof_indices, dsp, keep_constrained);\n      }\n    }\n\n\n    /**\n     * Given a @p dof_handler, and constraints @p affine_constraints this\n     * function creates an extended sparsity pattern for the discontinuous\n     * Galerkin formulation that also includes locally relevant to locally\n     * relevant couplings.\n     *\n     * @ingroup FiniteElement\n     */\n    template <int dim, typename Number, typename SPARSITY>\n    void make_extended_sparsity_pattern_dg(\n        const dealii::DoFHandler<dim> &dof_handler,\n        SPARSITY &dsp,\n        const dealii::AffineConstraints<Number> &affine_constraints,\n        bool keep_constrained)\n    {\n      Assert(affine_constraints.n_constraints() == 0,\n             dealii::ExcMessage(\"I don't think constraints make sense for dG\"));\n\n      std::vector<dealii::types::global_dof_index> dof_indices;\n      std::vector<dealii::types::global_dof_index> neighbor_dof_indices;\n\n      /*\n       * We collect all coupling dof indices on a face and store the result\n       * in a vector.\n       */\n      std::vector<dealii::types::global_dof_index> coupling_indices;\n      std::vector<dealii::types::global_dof_index> neighbor_coupling_indices;\n\n      /* we iterate over locally owned cells and the ghost layer */\n      for (auto cell : dof_handler.active_cell_iterators()) {\n        if (cell->is_artificial())\n          continue;\n\n        const unsigned int dofs_per_cell = cell->get_fe().n_dofs_per_cell();\n        dof_indices.resize(dofs_per_cell);\n        cell->get_dof_indices(dof_indices);\n\n        affine_constraints.add_entries_local_to_global(\n            dof_indices, dsp, keep_constrained);\n\n        for (const auto f_index : cell->face_indices()) {\n          const auto &face = cell->face(f_index);\n\n          /* Skip faces without neighbors... */\n          const bool has_neighbor =\n              !face->at_boundary() || cell->has_periodic_neighbor(f_index);\n          if (!has_neighbor)\n            continue;\n\n          /* Avoid artificial cells: */\n          const auto neighbor_cell =\n              cell->neighbor_or_periodic_neighbor(f_index);\n          if (neighbor_cell->is_artificial())\n            continue;\n\n          const unsigned int neighbor_dofs_per_cell =\n              neighbor_cell->get_fe().n_dofs_per_cell();\n          neighbor_dof_indices.resize(neighbor_dofs_per_cell);\n          neighbor_cell->get_dof_indices(neighbor_dof_indices);\n\n          const unsigned int f_index_neighbor =\n              cell->has_periodic_neighbor(f_index)\n                  ? cell->periodic_neighbor_of_periodic_neighbor(f_index)\n                  : cell->neighbor_of_neighbor(f_index);\n\n          /*\n           * Construct all couplings between current and neighbor cell with\n           * DoFs located at the boundary:\n           */\n\n          coupling_indices.resize(0);\n          for (unsigned int i = 0; i < dofs_per_cell; ++i)\n            if (cell->get_fe().has_support_on_face(i, f_index))\n              coupling_indices.push_back(dof_indices[i]);\n\n          neighbor_coupling_indices.resize(0);\n          for (unsigned int j = 0; j < neighbor_dofs_per_cell; ++j)\n            if (neighbor_cell->get_fe().has_support_on_face(j,\n                                                            f_index_neighbor))\n              neighbor_coupling_indices.push_back(neighbor_dof_indices[j]);\n\n          affine_constraints.add_entries_local_to_global(\n              coupling_indices,\n              neighbor_coupling_indices,\n              dsp,\n              keep_constrained);\n        }\n      }\n    }\n\n\n  } // namespace DoFTools\n\n} // namespace ryujin\n"
  },
  {
    "path": "source/loop.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception or LGPL-2.1-or-later\n// Copyright (C) 2025 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n#include <convenience_macros.h>\n#include <simd.h>\n\n#include <deal.II/base/config.h>\n#include <deal.II/base/parallel.h>\n\n#include <string>\n\n#ifdef WITH_OPENMP\n#include <omp.h>\n#endif\n\nnamespace ryujin\n{\n  /*\n   * A thread-parallelized and vectorized loop running on the CPU. The loop\n   * traverses the index range [left, internal) SIMD vectorized stepping\n   * forward with a stride size equal to the number of packed\n   * doubles/singles that the loop body operates on at the same time. For\n   * the remainder of the index range, i.e., [internal, right) a serial\n   * loop is invoked.\n   *\n   * @note the index internal is rounded down to the next integer multiple\n   * of the SIMD stride size.\n   *\n   * @note Here, @p body is a functor that must accept a \"sentinel\" type as\n   * first argument and the current index i as last argument. Additional\n   * `args` may be specified in the cpu_simd_loop() invocation that will be\n   * forwarded to the loop body:\n   * `body(Number(), std::forward<Args>(args)..., i);`\n   */\n  template <typename ScalarNumber, typename Functor, typename... Args>\n  inline void cpu_simd_loop(const std::string &region_name [[maybe_unused]],\n                            const Functor &body,\n                            const unsigned int left,\n                            const unsigned int internal,\n                            const unsigned int right,\n                            Args &&...args)\n  {\n    Assert(left <= internal && internal <= right,\n           dealii::ExcMessage(\"Invalid index range: it must hold left <= \"\n                              \"internal, internal <= right\"));\n\n    using VA = dealii::VectorizedArray<ScalarNumber>;\n\n    constexpr unsigned int stride_size = get_stride_size<VA>;\n    const unsigned int regular =\n        left + (internal - left) / stride_size * stride_size;\n\n#if defined(WITH_OPENMP)\n    /* Variant using OpenMP: */\n\n    RYUJIN_PRAGMA(omp parallel default(shared))\n    {\n      /* SIMD vectorized loop: */\n      RYUJIN_PRAGMA(omp for nowait)\n      for (unsigned int i = left; i < regular; i += stride_size)\n        body(VA(), std::forward<Args>(args)..., i);\n\n      /* Serial loop: */\n      RYUJIN_PRAGMA(omp for)\n      for (unsigned int i = regular; i < right; i += 1)\n        body(ScalarNumber(), std::forward<Args>(args)..., i);\n    }\n\n#elif defined(WITH_DEAL_II_THREADS)\n    /* Variant using dealii's parallel for: */\n    {\n      /*\n       * We have to ensure that the deal.II routine only schedules a\n       * workload that is divisible by stride_size.\n       */\n      Assert((regular - left) % stride_size == 0, dealii::ExcInternalError());\n      dealii::parallel::apply_to_subranges(\n          0,\n          (regular - left) / stride_size,\n          [&](const unsigned int begin, const unsigned int end) {\n            /* SIMD vectorized loop: */\n            for (unsigned int i = begin; i < end; ++i)\n              body(VA(), std::forward<Args>(args)..., left + stride_size * i);\n          },\n          1000);\n\n      dealii::parallel::apply_to_subranges(\n          regular,\n          right,\n          [&](const unsigned int begin, const unsigned int end) {\n            /* Serial loop: */\n            for (unsigned int i = begin; i < end; ++i)\n              body(ScalarNumber(), std::forward<Args>(args)..., i);\n          },\n          1000);\n    }\n\n#else\n    /* Execute loops in serial: */\n    {\n      /* SIMD vectorized loop: */\n      for (unsigned int i = left; i < regular; i += stride_size)\n        body(VA(), std::forward<Args>(args)..., i);\n\n      /* Serial loop: */\n      for (unsigned int i = regular; i < right; i += 1)\n        body(ScalarNumber(), std::forward<Args>(args)..., i);\n    }\n#endif\n  }\n} // namespace ryujin\n"
  },
  {
    "path": "source/main.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#include <compile_time_options.h>\n\n#include \"equation_dispatch.h\"\n#include \"instrumentation.h\"\n\n#include <deal.II/base/mpi.h>\n#include <deal.II/base/multithread_info.h>\n#include <deal.II/base/utilities.h>\n\n#ifdef WITH_OPENMP\n#include <omp.h>\n#endif\n\n#include <algorithm>\n#include <filesystem>\n\n/**\n * Change rounding mode on X86-64 architecture: Denormals are flushed to\n * zero to avoid computing on denormals which can slow down computations\n * significantly.\n */\nvoid flush_denormals_to_zero()\n{\n#if defined(DENORMALS_ARE_ZERO) && defined(__x86_64)\n#define MXCSR_DAZ (1 << 6)  /* Enable denormals are zero mode */\n#define MXCSR_FTZ (1 << 15) /* Enable flush to zero mode */\n\n  unsigned int mxcsr = __builtin_ia32_stmxcsr();\n  mxcsr |= MXCSR_DAZ | MXCSR_FTZ;\n  __builtin_ia32_ldmxcsr(mxcsr);\n#endif\n}\n\n\n/**\n * Set up thread pools and obey thread limits:\n */\nvoid set_thread_limit(const MPI_Comm &mpi_communicator [[maybe_unused]])\n{\n  unsigned int n_threads [[maybe_unused]] =\n      dealii::MultithreadInfo::n_threads();\n\n#ifdef WITH_OPENMP\n  const unsigned int omp_thread_limit = omp_get_thread_limit();\n  const unsigned int omp_max_threads = omp_get_max_threads();\n  n_threads = std::min({omp_thread_limit, omp_max_threads, n_threads});\n  omp_set_num_threads(n_threads);\n#endif\n\n#ifdef WITH_DEAL_II_THREADS\n  dealii::MultithreadInfo::set_thread_limit(n_threads);\n#else\n  dealii::MultithreadInfo::set_thread_limit(1);\n#endif\n}\n\n\n/**\n * The main function\n */\nint main(int argc, char *argv[])\n{\n  /*\n   * Entry point into the ryujin executable. We first determine what\n   * parameter file to use and then hand off everything to an\n   * EquationDispatch instance.\n   */\n\n  flush_denormals_to_zero();\n\n  LSAN_DISABLE;\n  dealii::Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv);\n  MPI_Comm mpi_communicator(MPI_COMM_WORLD);\n  set_thread_limit(mpi_communicator);\n  LSAN_ENABLE\n\n  LIKWID_MARKER_INIT;\n#ifdef WITH_OPENMP\n  RYUJIN_PRAGMA(omp parallel)\n  {\n    LIKWID_MARKER_THREAD_INIT;\n  }\n#endif\n\n  if (dealii::Utilities::MPI::this_mpi_process(mpi_communicator) == 0) {\n    std::cout << \"[INFO] initiating flux capacitor\" << std::endl;\n  }\n\n  if (argc > 2) {\n    if (dealii::Utilities::MPI::this_mpi_process(mpi_communicator) == 0) {\n      std::cout << \"[ERROR] Invalid number of parameters. At most one argument \"\n                << \"supported which has to be a parameter file.\" << std::endl;\n    }\n\n    LIKWID_MARKER_CLOSE;\n    LSAN_DISABLE;\n    return 1;\n  }\n\n  const auto executable_name = std::filesystem::path(argv[0]).filename();\n  std::string parameter_file = executable_name.string() + \".prm\";\n\n  if (argc == 2) {\n    parameter_file = argv[1];\n\n    if (!std::filesystem::exists(parameter_file)) {\n      if (dealii::Utilities::MPI::this_mpi_process(mpi_communicator) == 0) {\n        std::cout << \"[ERROR] The specified parameter file »\" << parameter_file\n                  << \"« does not exist.\" << std::endl;\n      }\n\n      LIKWID_MARKER_CLOSE;\n      LSAN_DISABLE;\n      return 1;\n    }\n  }\n\n  if (!std::filesystem::exists(parameter_file)) {\n    if (dealii::Utilities::MPI::this_mpi_process(mpi_communicator) == 0) {\n      std::cout << \"[INFO] Default parameter file »\" << parameter_file\n                << \"« not found.\\n[INFO] Creating template parameter files for \"\n                << \"you. Please modify and rename one of the templates to »\"\n                << parameter_file << \"«.\" << std::endl;\n      ryujin::EquationDispatch::create_parameter_files();\n    }\n\n    MPI_Barrier(mpi_communicator);\n\n    LIKWID_MARKER_CLOSE;\n    LSAN_DISABLE;\n    return 1;\n  }\n\n  {\n    ryujin::EquationDispatch equation_dispatch;\n    equation_dispatch.dispatch(parameter_file, mpi_communicator);\n  }\n\n  LIKWID_MARKER_CLOSE;\n  LSAN_DISABLE;\n  return 0;\n}\n"
  },
  {
    "path": "source/mesh_adaptor.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2024 by the ryujin authors\n//\n\n#include \"mesh_adaptor.template.h\"\n#include <instantiate.h>\n\nnamespace ryujin\n{\n  /* instantiations */\n  template class MeshAdaptor<Description, 1, NUMBER>;\n  template class MeshAdaptor<Description, 2, NUMBER>;\n  template class MeshAdaptor<Description, 3, NUMBER>;\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/mesh_adaptor.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2024 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"mpi_ensemble.h\"\n#include \"observer_pointer.h\"\n#include \"offline_data.h\"\n\n#include <deal.II/base/parameter_acceptor.h>\n\n#include <random>\n\nnamespace ryujin\n{\n  /**\n   * Controls the spatial mesh adaptation strategy.\n   *\n   * @ingroup Mesh\n   */\n  enum class AdaptationStrategy {\n    /**\n     * Perform a uniform global refinement.\n     */\n    global_refinement,\n\n    /**\n     * Perform random refinement and coarsening with a deterministic\n     * Mersenne Twister and a chosen seed. This refinement strategy is only\n     * useful for debugging and testing.\n     */\n    random_adaptation,\n\n    /**\n     * Perform local refinement and coarsening based on smoothness indicators.\n     */\n    smoothness_indicators,\n  };\n\n  /**\n   * Controls the marking strategy used for mesh adaptation. This\n   * configuration option is ignored for the uniform global refinement\n   * strategy.\n   *\n   * @ingroup Mesh\n   */\n  enum class MarkingStrategy {\n    /**\n     * Refine and coarsen according to a fixed tolerance normalized\n     * according to the difference between the maximal and minimal attained\n     * values for the chosen refinement indicators.\n     */\n    fixed_threshold,\n  };\n\n  /**\n   * Controls the time point selection strategy.\n   *\n   * @ingroup Mesh\n   */\n  enum class TimePointSelectionStrategy {\n    /**\n     * Perform a mesh adaptation cycle at preselected fixed time points.\n     */\n    fixed_time_points,\n\n    /**\n     * Perform a mesh adaptation cycle at every nth simulation cycle.\n     */\n    simulation_cycle,\n  };\n} // namespace ryujin\n\n#ifndef DOXYGEN\nDECLARE_ENUM(\n    ryujin::AdaptationStrategy,\n    LIST({ryujin::AdaptationStrategy::global_refinement, \"global refinement\"},\n         {ryujin::AdaptationStrategy::random_adaptation, \"random adaptation\"},\n         {ryujin::AdaptationStrategy::smoothness_indicators,\n          \"smoothness indicators\"}, ));\n\nDECLARE_ENUM(ryujin::MarkingStrategy,\n             LIST({ryujin::MarkingStrategy::fixed_threshold,\n                   \"fixed threshold\"}));\n\nDECLARE_ENUM(ryujin::TimePointSelectionStrategy,\n             LIST({ryujin::TimePointSelectionStrategy::fixed_time_points,\n                   \"fixed time points\"},\n                  {ryujin::TimePointSelectionStrategy::simulation_cycle,\n                   \"simulation cycle\"}, ));\n#endif\n\nnamespace ryujin\n{\n  /**\n   * The MeshAdaptor class is responsible for performing global or local\n   * mesh adaptation.\n   *\n   * @ingroup Mesh\n   */\n  template <typename Description, int dim, typename Number = double>\n  class MeshAdaptor final : public dealii::ParameterAcceptor\n  {\n  public:\n    /**\n     * @name Typedefs and constexpr constants\n     */\n    //@{\n\n    using HyperbolicSystem = typename Description::HyperbolicSystem;\n    using ParabolicSystem = typename Description::ParabolicSystem;\n\n    using View =\n        typename Description::template HyperbolicSystemView<dim, Number>;\n\n    static constexpr auto problem_dimension = View::problem_dimension;\n\n    using StateVector = typename View::StateVector;\n    using InitialPrecomputedVector = typename View::InitialPrecomputedVector;\n    using ScalarVector = Vectors::ScalarVector<Number>;\n\n    //@}\n    /**\n     * @name Constructor and setup\n     */\n    //@{\n\n    /**\n     * Constructor.\n     */\n    MeshAdaptor(const MPIEnsemble &mpi_ensemble,\n                const OfflineData<dim, Number> &offline_data,\n                const HyperbolicSystem &hyperbolic_system,\n                const ParabolicSystem &parabolic_system,\n                const InitialPrecomputedVector &initial_precomputed,\n                const ScalarVector &alpha,\n                const std::string &subsection = \"/MeshAdaptor\");\n\n    /**\n     * Prepare temporary storage and clean up internal data for the\n     * analyze() facility.\n     */\n    void prepare(const Number t);\n\n    /**\n     * Analyze the given StateVector with the configured adaptation\n     * strategy and time point selection strategy and decide whether a mesh\n     * adaptation cycle should be performed.\n     */\n    void analyze(const StateVector &state_vector,\n                 const Number t,\n                 unsigned int cycle);\n\n    /**\n     * A boolean indicating whether we should perform a mesh adapation step\n     * in the current cycle. The analyze() method will set this boolean to\n     * true whenever the selected adaptation strategy advices to perform an\n     * adaptation cycle.\n     */\n    ACCESSOR_READ_ONLY(need_mesh_adaptation)\n\n    /**\n     * Mark cells for coarsening and refinement with the configured mesh\n     * adaptation and marking strategies.\n     */\n    void mark_cells_for_coarsening_and_refinement(\n        dealii::Triangulation<dim> &triangulation) const;\n\n    /**\n     * The computed cell indicators.\n     */\n    ACCESSOR_READ_ONLY(indicators);\n\n    /**\n     * Compute smoothness indicators. This function reinitializes and\n     * populates the smoothness_indicators() vector.\n     */\n    void compute_smoothness_indicators(const StateVector &state_vector) const;\n\n    /**\n     * The computed smoothness indicators. The vector is only valid if the\n     * \"smoothness indicator\" refinement strategy has been selected.\n     */\n    ACCESSOR_READ_ONLY(smoothness_indicators);\n\n  private:\n    /**\n     * @name Run time options\n     */\n    //@{\n\n    AdaptationStrategy adaptation_strategy_;\n    std::uint_fast64_t random_adaptation_mersenne_twister_seed_;\n\n    MarkingStrategy marking_strategy_;\n    double coarsening_threshold_;\n    double refinement_threshold_;\n    bool absolute_threshold_;\n    unsigned int min_refinement_level_;\n    unsigned int max_refinement_level_;\n\n    TimePointSelectionStrategy time_point_selection_strategy_;\n    std::vector<Number> adaptation_time_points_;\n    unsigned int adaptation_cycle_interval_;\n\n    std::vector<std::string> smoothness_selected_quantities_;\n    Number smoothness_local_global_ratio_;\n    Number smoothness_min_cutoff_;\n    Number smoothness_max_cutoff_;\n    unsigned int smoothness_widen_stencil_;\n\n    //@}\n    /**\n     * @name Internal fields and methods\n     */\n    //@{\n\n    const MPIEnsemble &mpi_ensemble_;\n\n    dealii::ObserverPointer<const OfflineData<dim, Number>> offline_data_;\n    dealii::ObserverPointer<const HyperbolicSystem> hyperbolic_system_;\n    dealii::ObserverPointer<const ParabolicSystem> parabolic_system_;\n\n    const InitialPrecomputedVector &initial_precomputed_;\n    const ScalarVector &alpha_;\n\n    bool need_mesh_adaptation_;\n\n    mutable dealii::Vector<float> indicators_;\n\n    /* random adaptation: */\n\n    void populate_cell_indicators_with_random_values() const;\n\n    mutable std::mt19937_64 mersenne_twister_;\n\n    /* Smoothness indicator: */\n\n    void populate_cell_indicators_from_smoothness_indicators() const;\n\n    mutable ScalarVector smoothness_indicators_;\n    //@}\n  };\n\n} // namespace ryujin\n"
  },
  {
    "path": "source/mesh_adaptor.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2024 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include \"loop.h\"\n#include \"mesh_adaptor.h\"\n#include \"mpi_ensemble.h\"\n#include \"selected_components_extractor.h\"\n#include \"simd.h\"\n\n#include <deal.II/base/array_view.h>\n#include <deal.II/distributed/grid_refinement.h>\n\n#include <boost/container/small_vector.hpp>\n\nnamespace ryujin\n{\n  template <typename Description, int dim, typename Number>\n  MeshAdaptor<Description, dim, Number>::MeshAdaptor(\n      const MPIEnsemble &mpi_ensemble,\n      const OfflineData<dim, Number> &offline_data,\n      const HyperbolicSystem &hyperbolic_system,\n      const ParabolicSystem &parabolic_system,\n      const InitialPrecomputedVector &initial_precomputed,\n      const ScalarVector &alpha,\n      const std::string &subsection /*= \"MeshAdaptor\"*/)\n      : ParameterAcceptor(subsection)\n      , mpi_ensemble_(mpi_ensemble)\n      , offline_data_(&offline_data)\n      , hyperbolic_system_(&hyperbolic_system)\n      , parabolic_system_(&parabolic_system)\n      , initial_precomputed_(initial_precomputed)\n      , alpha_(alpha)\n      , need_mesh_adaptation_(false)\n  {\n    adaptation_strategy_ = AdaptationStrategy::smoothness_indicators;\n    add_parameter(\"adaptation strategy\",\n                  adaptation_strategy_,\n                  \"The chosen adaptation strategy. Possible values are: global \"\n                  \"refinement, random adaptation, smoothness indicators\");\n\n    marking_strategy_ = MarkingStrategy::fixed_threshold;\n    add_parameter(\n        \"marking strategy\",\n        marking_strategy_,\n        \"The chosen marking strategy. Possible values are: fixed threshold.\");\n\n    time_point_selection_strategy_ =\n        TimePointSelectionStrategy::simulation_cycle;\n    add_parameter(\"time point selection strategy\",\n                  time_point_selection_strategy_,\n                  \"The chosen time point selection strategy. Possible values \"\n                  \"are: fixed time points, simulation cycle\");\n\n    /* Options for various adaptation strategies: */\n    enter_subsection(\"adaptation strategies\");\n    random_adaptation_mersenne_twister_seed_ = 42u;\n    add_parameter(\"random adaptation: mersenne_twister_seed\",\n                  random_adaptation_mersenne_twister_seed_,\n                  \"Seed for 64bit Mersenne Twister used for random refinement\");\n\n    add_parameter(\n        \"smoothness indicators: quantities\",\n        smoothness_selected_quantities_,\n        \"List of conserved, primitive or precomputed quantities that will be \"\n        \"used for constructing the smoothness indicator.\");\n\n    smoothness_local_global_ratio_ = 0.5;\n    add_parameter(\n        \"smoothness indicators: local global ratio\",\n        smoothness_local_global_ratio_,\n        \"Ratio between local and global denominator value used for normalizing \"\n        \"the smoothness indicator. A value of 1 indicates pure global \"\n        \"normalization, a value of 0 indicates pure local normalization.\");\n\n    smoothness_min_cutoff_ = 0.0;\n    add_parameter(\"smoothness indicators: min cutoff\",\n                  smoothness_min_cutoff_,\n                  \"minimal cutoff for the smoothness indicator: values below \"\n                  \"this threshold will be set to the cutoff value.\");\n\n    smoothness_max_cutoff_ = 1.0e16;\n    add_parameter(\"smoothness indicators: max cutoff\",\n                  smoothness_max_cutoff_,\n                  \"minimal cutoff for the smoothness indicator: values above \"\n                  \"this threshold will be set to the cutoff value.\");\n\n    smoothness_widen_stencil_ = 15;\n    add_parameter(\n        \"smoothness indicators: stencil size\",\n        smoothness_widen_stencil_,\n        \"Number of layers to widen the smoothness indicator stencil.\");\n    leave_subsection();\n\n    /* Options for various marking strategies: */\n\n    enter_subsection(\"marking strategies\");\n\n    coarsening_threshold_ = 0.25;\n    add_parameter(\n        \"coarsening threshold\",\n        coarsening_threshold_,\n        \"Marking: normalized or absolute threshold for selecting cells for \"\n        \"coarsening (used in \\\"fixed threshhold\\\" marking strategy).\");\n\n    refinement_threshold_ = 0.75;\n    add_parameter(\n        \"refinement threshold\",\n        refinement_threshold_,\n        \"Marking: normalized or absolute threshold for selecting cells for \"\n        \"refinement (used in \\\"fixed threshold\\\" marking strategy).\");\n\n    absolute_threshold_ = false;\n    add_parameter(\"absolute threshold\",\n                  absolute_threshold_,\n                  \"Marking: if set to true use an absolute threshold for the \"\n                  \"\\\"refinement threshold\\\" and \\\"coarsening threshold\\\" \"\n                  \"values instead of a relative one. If this parameter is set \"\n                  \"to false then the smoothness indicator is normalized into \"\n                  \"the number range of [0., 1] and the threshold parameters \"\n                  \"are also expected to be a value in this interval.\");\n\n    min_refinement_level_ = 0;\n    add_parameter(\"minimal refinement level\",\n                  min_refinement_level_,\n                  \"Marking: minimal refinement level of cells that will be \"\n                  \"maintained while coarsening cells.\");\n\n    max_refinement_level_ = 1000;\n    add_parameter(\"maximal refinement level\",\n                  max_refinement_level_,\n                  \"Marking: maximal refinement level of cells that will be \"\n                  \"maintained while refininig cells.\");\n    leave_subsection();\n\n    /* Options for various time point selection strategies: */\n\n    enter_subsection(\"time point selection strategies\");\n    adaptation_time_points_ = {};\n    add_parameter(\"fixed time points\",\n                  adaptation_time_points_,\n                  \"List of time points in (simulation) time at which we will \"\n                  \"perform a mesh adaptation cycle.\");\n\n    adaptation_cycle_interval_ = 10;\n    add_parameter(\"simulation cycle: interval\",\n                  adaptation_cycle_interval_,\n                  \"The nth simulation cycle at which we will \"\n                  \"perform mesh adapation.\");\n    leave_subsection();\n\n    const auto call_back = [this] {\n      /* Initialize Mersenne Twister with configured seed: */\n      mersenne_twister_.seed(random_adaptation_mersenne_twister_seed_);\n    };\n\n    call_back();\n    ParameterAcceptor::parse_parameters_call_back.connect(call_back);\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void MeshAdaptor<Description, dim, Number>::prepare(const Number t)\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"MeshAdaptor<dim, Number>::prepare()\" << std::endl;\n#endif\n\n    if (time_point_selection_strategy_ ==\n        TimePointSelectionStrategy::fixed_time_points) {\n      /* Remove outdated refinement timestamps: */\n      const auto new_end = std::remove_if(\n          adaptation_time_points_.begin(),\n          adaptation_time_points_.end(),\n          [&](const Number &t_refinement) { return (t > t_refinement); });\n      adaptation_time_points_.erase(new_end, adaptation_time_points_.end());\n    }\n\n    SelectedComponentsExtractor<Description, dim, Number>::check(\n        parabolic_system_->parabolic_component_names(),\n        {\"alpha\"},\n        smoothness_selected_quantities_);\n\n    /* toggle mesh adaptation flag to off. */\n    need_mesh_adaptation_ = false;\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void MeshAdaptor<Description, dim, Number>::compute_smoothness_indicators(\n      const StateVector &state_vector) const\n  {\n    const auto &affine_constraints = offline_data_->affine_constraints();\n    const unsigned int n_internal = offline_data_->n_locally_internal();\n    const unsigned int n_owned = offline_data_->n_locally_owned();\n    const auto &sparsity_simd = offline_data_->sparsity_pattern_simd();\n    const auto &betaij_matrix = offline_data_->betaij_matrix();\n    using VA = dealii::VectorizedArray<Number>;\n\n    /*\n     * Extract selected quantities:\n     */\n\n    auto quantities =\n        SelectedComponentsExtractor<Description, dim, Number>::extract(\n            *offline_data_,\n            *hyperbolic_system_,\n            *parabolic_system_,\n            state_vector,\n            initial_precomputed_,\n            {\"alpha\"},\n            {alpha_},\n            smoothness_selected_quantities_);\n\n    for (auto &it : quantities) {\n      it.update_ghost_values();\n      affine_constraints.distribute(it);\n      it.update_ghost_values();\n    }\n\n    /*\n     * Set up temporary vectors:\n     */\n\n    const unsigned int n_entries = quantities.size();\n    const auto &scalar_partitioner = offline_data_->scalar_partitioner();\n\n    using ScalarHostVector = Vectors::ScalarHostVector<Number>;\n    /*\n     * Ensure we have at least one entry in numerator and denominator\n     * available. We use those for temporary storage.\n     */\n    std::vector<ScalarHostVector> numerator(std::max(1u, n_entries));\n    std::vector<ScalarHostVector> denominator(std::max(1u, n_entries));\n    for (auto &it : numerator)\n      it.reinit(scalar_partitioner);\n    for (auto &it : denominator)\n      it.reinit(scalar_partitioner);\n\n    /*\n     * Commpute numerators and denominators for the smoothness indicators:\n     */\n\n    const auto body = [&](auto sentinel, unsigned int i) {\n      using T = decltype(sentinel);\n      unsigned int stride_size = get_stride_size<T>;\n\n      /* Skip constrained degrees of freedom: */\n      const unsigned int row_length = sparsity_simd.row_length(i);\n      if (row_length == 1)\n        return;\n\n      boost::container::small_vector<T, 10> value_i(n_entries, T(0.));\n      for (unsigned int k = 0; k < n_entries; ++k) {\n        value_i[k] = read_entry<T>(quantities[k], i);\n      }\n\n      boost::container::small_vector<T, 10> numerator_i(n_entries, T(0.));\n      boost::container::small_vector<T, 10> denominator_i(n_entries, T(0.));\n\n      const unsigned int *js = sparsity_simd.columns(i);\n      for (unsigned int col_idx = 0; col_idx < row_length;\n           ++col_idx, js += stride_size) {\n\n        /* Skip diagonal. */\n        if (col_idx == 0)\n          continue;\n\n        const auto beta_ij = betaij_matrix.template read_entry<T>(i, col_idx);\n\n        for (unsigned int k = 0; k < n_entries; ++k) {\n          const auto value_j_k = read_entry<T>(quantities[k], js);\n          numerator_i[k] += beta_ij * (value_j_k - value_i[k]);\n          denominator_i[k] +=\n              std::abs(beta_ij) * //\n              std::max(std::abs(value_j_k), std::abs(value_i[k]));\n        }\n\n        for (unsigned int k = 0; k < n_entries; ++k) {\n          write_entry<T>(numerator[k], numerator_i[k], i);\n          write_entry<T>(denominator[k], denominator_i[k], i);\n        }\n      }\n    };\n\n    cpu_simd_loop<Number>(\"mesh_adaptor_1\", body, 0, n_internal, n_owned);\n\n    /*\n     * Sum up and normalize the indicators and store the result in numerator[0]:\n     */\n\n    constexpr Number eps = std::numeric_limits<Number>::epsilon();\n\n    std::vector<Number> denominator_global_maximum(n_entries);\n    for (unsigned int k = 0; k < n_entries; ++k) {\n      denominator_global_maximum[k] = dealii::Utilities::MPI::max(\n          denominator[k].linfty_norm(), mpi_ensemble_.ensemble_communicator());\n\n      denominator_global_maximum[k] =\n          std::max(denominator_global_maximum[k], eps);\n    }\n\n    const auto body_normalize = [&](auto sentinel, unsigned int i) {\n      using T = decltype(sentinel);\n\n      /* Skip constrained degrees of freedom: */\n      const unsigned int row_length = sparsity_simd.row_length(i);\n      if (row_length == 1)\n        return;\n\n      auto alpha_i = T(0.);\n      for (unsigned int k = 0; k < n_entries; ++k) {\n        const auto numerator_i = read_entry<T>(numerator[k], i);\n        const auto denominator_i = read_entry<T>(denominator[k], i);\n\n        auto denominator =\n            (Number(1.) - smoothness_local_global_ratio_) * denominator_i +\n            smoothness_local_global_ratio_ * denominator_global_maximum[k];\n        denominator = std::max(T(eps), denominator);\n\n        alpha_i += std::abs(numerator_i) / denominator;\n      }\n\n      alpha_i = std::min(alpha_i, T(smoothness_max_cutoff_));\n      alpha_i = std::max(alpha_i, T(smoothness_min_cutoff_));\n      write_entry<T>(/*SIC!*/ numerator[0], alpha_i, i);\n    };\n\n    cpu_simd_loop<Number>(\n        \"mesh_adaptor_2\", body_normalize, 0, n_internal, n_owned);\n\n    /*\n     * Widen indicators over stencil via max() operator:\n     */\n\n    const auto body_widen = [&](auto sentinel, unsigned int i) {\n      using T = decltype(sentinel);\n      unsigned int stride_size = get_stride_size<T>;\n\n      /* Skip constrained degrees of freedom: */\n      const unsigned int row_length = sparsity_simd.row_length(i);\n      if (row_length == 1)\n        return;\n\n      auto alpha_i = read_entry<T>(numerator[0], i);\n\n      const unsigned int *js = sparsity_simd.columns(i);\n      for (unsigned int col_idx = 0; col_idx < row_length;\n           ++col_idx, js += stride_size) {\n\n        /* Skip diagonal. */\n        if (col_idx == 0)\n          continue;\n\n        const auto alpha_j = read_entry<T>(numerator[0], js);\n\n        alpha_i = std::max(alpha_i, alpha_j);\n      }\n\n      write_entry<T>(/*SIC!*/ denominator[0], alpha_i, i);\n    };\n\n    for (unsigned int cycle = 0; cycle < smoothness_widen_stencil_; ++cycle) {\n      numerator[0].update_ghost_values();\n      cpu_simd_loop<Number>(\n          \"mesh_adaptor_3\", body_widen, 0, n_internal, n_owned);\n      numerator[0] = /*SIC!*/ denominator[0];\n    }\n\n    numerator[0].update_ghost_values();\n    affine_constraints.distribute(numerator[0]);\n\n    /*\n     * Insert result into smoothness_indicators_:\n     */\n\n    smoothness_indicators_.reinit_with_scalar_partitioner(scalar_partitioner);\n    smoothness_indicators_.insert_component(numerator[0], 0);\n    smoothness_indicators_.update_ghost_values();\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void MeshAdaptor<Description, dim, Number>::analyze(\n      const StateVector &state_vector, const Number t, unsigned int cycle)\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"MeshAdaptor<dim, Number>::analyze()\" << std::endl;\n#endif\n\n    /*\n     * Decide whether we perform an adaptation cycle with the chosen time\n     * point selection strategy:\n     */\n\n    switch (time_point_selection_strategy_) {\n    case TimePointSelectionStrategy::fixed_time_points: {\n      /* Remove all refinement points from the vector that lie in the past: */\n      const auto new_end = std::remove_if( //\n          adaptation_time_points_.begin(),\n          adaptation_time_points_.end(),\n          [&](const Number &t_refinement) {\n            if (t < t_refinement)\n              return false;\n            need_mesh_adaptation_ = true;\n            return true;\n          });\n      adaptation_time_points_.erase(new_end, adaptation_time_points_.end());\n    } break;\n\n    case TimePointSelectionStrategy::simulation_cycle: {\n      /* check whether we reached a cycle interval: */\n      if (cycle % adaptation_cycle_interval_ == 0)\n        need_mesh_adaptation_ = true;\n    } break;\n\n    default:\n      AssertThrow(false, dealii::ExcInternalError());\n      __builtin_trap();\n    }\n\n    if (!need_mesh_adaptation_)\n      return;\n\n    /*\n     * Some adaptation strategies require us to prepare some internal\n     * data fields:\n     */\n\n    switch (adaptation_strategy_) {\n    case AdaptationStrategy::global_refinement:\n      /* do nothing */\n      break;\n\n    case AdaptationStrategy::random_adaptation:\n      /* do nothing */\n      break;\n\n    case AdaptationStrategy::smoothness_indicators: {\n      compute_smoothness_indicators(state_vector);\n      break;\n    }\n\n    default:\n      AssertThrow(false, dealii::ExcInternalError());\n      __builtin_trap();\n    }\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void MeshAdaptor<Description, dim, Number>::\n      populate_cell_indicators_with_random_values() const\n  {\n    std::generate(std::begin(indicators_), std::end(indicators_), [&]() {\n      static std::uniform_real_distribution<double> distribution(0.0, 10.0);\n      return distribution(mersenne_twister_);\n    });\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void MeshAdaptor<Description, dim, Number>::\n      populate_cell_indicators_from_smoothness_indicators() const\n  {\n    const auto &scalar_partitioner = offline_data_->scalar_partitioner();\n\n    /*\n     * Distribute to cells by taking a cell-wise average:\n     */\n\n    std::vector<dealii::types::global_dof_index> local_dof_indices;\n\n    const auto &dof_handler = offline_data_->dof_handler();\n    for (const auto &cell : dof_handler.active_cell_iterators()) {\n      if (!cell->is_locally_owned())\n        continue;\n\n      const unsigned int dofs_per_cell = cell->get_fe().n_dofs_per_cell();\n      const auto scale = Number(1. / dofs_per_cell);\n      local_dof_indices.resize(dofs_per_cell);\n      cell->get_dof_indices(local_dof_indices);\n\n      auto alpha_cell = Number(0.);\n\n      for (unsigned int i = 0; i < dofs_per_cell; ++i) {\n        const auto global_i = local_dof_indices[i];\n        const auto local_i = scalar_partitioner->global_to_local(global_i);\n        auto alpha_i =\n            smoothness_indicators_.template read_entry<Number>(local_i);\n        alpha_cell += alpha_i;\n      }\n      alpha_cell *= scale;\n\n      indicators_[cell->active_cell_index()] = static_cast<float>(alpha_cell);\n    }\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void MeshAdaptor<Description, dim, Number>::\n      mark_cells_for_coarsening_and_refinement(\n          dealii::Triangulation<dim> &triangulation [[maybe_unused]]) const\n  {\n    auto &discretization [[maybe_unused]] = offline_data_->discretization();\n    Assert(&triangulation == &discretization.triangulation(),\n           dealii::ExcInternalError());\n\n#if !DEAL_II_VERSION_GTE(9, 6, 0)\n    AssertThrow(\n        false,\n        dealii::ExcMessage(\n            \"The MeshAdaptor class needs deal.II version 9.6.0 or newer\"));\n\n#else\n\n    /*\n     * Compute cell indicators with the chosen adaptation strategy:\n     */\n\n    switch (adaptation_strategy_) {\n    case AdaptationStrategy::global_refinement: {\n      /* Simply mark all cells for refinement and return: */\n      for (auto &cell : triangulation.active_cell_iterators())\n        cell->set_refine_flag();\n      return;\n    } break;\n\n    case AdaptationStrategy::random_adaptation: {\n      indicators_.reinit(triangulation.n_active_cells());\n      populate_cell_indicators_with_random_values();\n    } break;\n\n    case AdaptationStrategy::smoothness_indicators: {\n      indicators_.reinit(triangulation.n_active_cells());\n      populate_cell_indicators_from_smoothness_indicators();\n    } break;\n\n    default:\n      AssertThrow(false, dealii::ExcInternalError());\n      __builtin_trap();\n    }\n\n    /*\n     * Mark cells with chosen marking strategy:\n     */\n\n    switch (marking_strategy_) {\n    case MarkingStrategy::fixed_threshold: {\n\n      float inv_denominator = 1.f;\n      float bias = 0.f;\n\n      if (!absolute_threshold_) {\n        /*\n         * Normalize indicators to the interval [0., 1.]\n         */\n\n        float minimum = std::numeric_limits<float>::max();\n        float maximum = 0.f;\n        for (const auto &cell : triangulation.active_cell_iterators()) {\n          if (!cell->is_locally_owned())\n            continue;\n          const auto indicator = indicators_[cell->active_cell_index()];\n          minimum = std::min(minimum, indicator);\n          maximum = std::max(maximum, indicator);\n        }\n        minimum = dealii::Utilities::MPI::min(\n            minimum, mpi_ensemble_.ensemble_communicator());\n        maximum = dealii::Utilities::MPI::max(\n            maximum, mpi_ensemble_.ensemble_communicator());\n\n        constexpr float eps = std::numeric_limits<float>::epsilon();\n        // Ensure that if minimum == maximum we end up with 0.5 everywhere\n        inv_denominator = 1.f / (maximum - minimum + 10.f * eps);\n        bias = (minimum + 5.f * eps) * inv_denominator;\n      }\n\n      /*\n       * And mark all cells according to threshold:\n       */\n\n      for (const auto &cell : triangulation.active_cell_iterators()) {\n        if (!cell->is_locally_owned())\n          continue;\n\n        auto indicator = indicators_[cell->active_cell_index()];\n        indicator = indicator * inv_denominator - bias;\n        if (indicator < coarsening_threshold_)\n          cell->set_coarsen_flag();\n        else if (indicator > refinement_threshold_)\n          cell->set_refine_flag();\n      }\n    } break;\n\n    default:\n      AssertThrow(false, dealii::ExcInternalError());\n      __builtin_trap();\n    }\n\n    /*\n     * Constrain refinement and coarsening to maximum and minimum\n     * refinement levels:\n     */\n\n    if (triangulation.n_levels() > max_refinement_level_)\n      for (const auto &cell :\n           triangulation.active_cell_iterators_on_level(max_refinement_level_))\n        cell->clear_refine_flag();\n\n    for (const auto &cell :\n         triangulation.active_cell_iterators_on_level(min_refinement_level_))\n      cell->clear_coarsen_flag();\n#endif\n  }\n} // namespace ryujin\n"
  },
  {
    "path": "source/mpi_ensemble.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2024 - 2025 by the ryujin authors\n//\n\n#include \"mpi_ensemble.h\"\n\n#ifdef DEBUG_OUTPUT\n#include <iostream>\n#endif\n\nnamespace ryujin\n{\n  MPIEnsemble::MPIEnsemble(const MPI_Comm &mpi_communicator,\n                           const int n_ensembles /* = 1 */,\n                           const bool global_synchronization /* = true */)\n      : world_communicator_(mpi_communicator)\n      , global_synchronization_(true)\n      , world_rank_(0)\n      , n_world_ranks_(1)\n      , ensemble_(0)\n      , n_ensembles_(1)\n      , ensemble_rank_(0)\n      , n_ensemble_ranks_(1)\n      , world_group_(MPI_GROUP_NULL)\n      , ensemble_leader_group_(MPI_GROUP_NULL)\n      , ensemble_communicator_(MPI_COMM_NULL)\n      , ensemble_leader_communicator_(MPI_COMM_NULL)\n      , peer_communicator_(MPI_COMM_NULL)\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"MPIEnsemble::prepare()\" << std::endl;\n#endif\n\n    n_ensembles_ = n_ensembles;\n    global_synchronization_ = global_synchronization;\n\n    world_rank_ = dealii::Utilities::MPI::this_mpi_process(world_communicator_);\n    n_world_ranks_ =\n        dealii::Utilities::MPI::n_mpi_processes(world_communicator_);\n\n    AssertThrow(n_ensembles_ > 0, dealii::ExcInternalError());\n    ensemble_ = world_rank_ % n_ensembles_;\n\n    /*\n     * Special case for MPI_COMM_SELF:\n     *\n     * If the ensemble is initialized with the MPI_COMM_SELF communicator,\n     * skip setting up the communicators for the case that n_ensembles_ > 1.\n     * This only serves the purpose that we can create a TimeLoop instance\n     * long enough to query for parameters.\n     */\n\n    Assert(MPI_COMM_SELF != MPI_COMM_WORLD,\n           dealii::ExcMessage(\n               \"MPIEnsemble: Internal sanity check failed. MPI_COMM_SELF and \"\n               \"MPI_COMM_WORLD are the same object.\"));\n\n    if (mpi_communicator == MPI_COMM_SELF && n_ensembles_ > 1)\n      return;\n\n    AssertThrow(\n        n_world_ranks_ >= n_ensembles_,\n        dealii::ExcMessage(\n            \"The number of (global) MPI processes must be equal to or larger \"\n            \"than the number of ensembles. But we are trying to run \" +\n            std::to_string(n_ensembles_) + \" ensembles on \" +\n            std::to_string(n_world_ranks_) + \" MPI processes.\"));\n\n    /*\n     * For each ensemble we create an MPI group with a ensemble-local\n     * communicator. This allows to do ensemble-independent time-stepping.\n     */\n\n    auto ierr = MPI_Comm_group(world_communicator_, &world_group_);\n    AssertThrowMPI(ierr);\n\n    /* subrange communicator: */\n\n    ensemble_groups_.resize(n_ensembles_);\n    for (int ensemble = 0; ensemble < n_ensembles_; ++ensemble) {\n      int ranges[1][3]{{ensemble, n_world_ranks_ - 1, n_ensembles_}}; // NOLINT\n      ierr = MPI_Group_range_incl(\n          world_group_, 1, ranges, &ensemble_groups_[ensemble]);\n      AssertThrowMPI(ierr);\n    }\n\n    ierr = MPI_Comm_create_group(world_communicator_,\n                                 ensemble_groups_[ensemble_],\n                                 ensemble_,\n                                 &ensemble_communicator_);\n    AssertThrowMPI(ierr);\n\n    /* subrange leader communicator: */\n\n    ensemble_rank_ =\n        dealii::Utilities::MPI::this_mpi_process(ensemble_communicator_);\n    n_ensemble_ranks_ =\n        dealii::Utilities::MPI::n_mpi_processes(ensemble_communicator_);\n    AssertThrow(\n        (ensemble_rank_ == 0 && world_rank_ < n_ensembles_) ||\n            (ensemble_rank_ > 0 && world_rank_ >= n_ensembles_),\n        dealii::ExcMessage(\"MPI Ensemble: Something went horribly wrong: Could \"\n                           \"not determine subrange leader.\"));\n\n    int ranges[1][3]{{0, n_ensembles_ - 1, 1}}; // NOLINT\n    ierr =\n        MPI_Group_range_incl(world_group_, 1, ranges, &ensemble_leader_group_);\n    AssertThrowMPI(ierr);\n\n    ierr = MPI_Comm_create(world_communicator_,\n                           ensemble_leader_group_,\n                           &ensemble_leader_communicator_);\n\n    /* peer communicator: */\n\n    ierr = MPI_Comm_split(\n        world_communicator_, ensemble_rank_, ensemble_, &peer_communicator_);\n    AssertThrowMPI(ierr);\n\n#ifdef DEBUG_OUTPUT\n    const auto peer_rank =\n        dealii::Utilities::MPI::this_mpi_process(peer_communicator_);\n    const auto n_peer_ranks =\n        dealii::Utilities::MPI::n_mpi_processes(peer_communicator_);\n    std::cout << \"RANK: (w = \" << world_rank_ << \", e = \" << ensemble_rank_\n              << \", p = \" << peer_rank << \") out of (w = \" << n_world_ranks_\n              << \", e = \" << n_ensemble_ranks_ << \", p = \" << n_peer_ranks\n              << \") -> belongig to ensemble: \" << ensemble_ << std::endl;\n#endif\n  }\n\n  MPIEnsemble::~MPIEnsemble()\n  {\n    if (world_group_ != MPI_GROUP_NULL)\n      MPI_Group_free(&world_group_);\n    for (auto &it : ensemble_groups_)\n      MPI_Group_free(&it);\n    if (ensemble_leader_group_ != MPI_GROUP_NULL)\n      MPI_Group_free(&ensemble_leader_group_);\n\n    if (ensemble_communicator_ != MPI_COMM_NULL)\n      MPI_Comm_free(&ensemble_communicator_);\n    if (ensemble_leader_communicator_ != MPI_COMM_NULL)\n      MPI_Comm_free(&ensemble_leader_communicator_);\n    if (peer_communicator_ != MPI_COMM_NULL)\n      MPI_Comm_free(&peer_communicator_);\n  }\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/mpi_ensemble.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2024 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"convenience_macros.h\"\n\n#include <deal.II/base/mpi.h>\n#include <deal.II/base/utilities.h>\n\nnamespace ryujin\n{\n  /**\n   * A class responsible for subdividing a given global MPI communicator\n   * into a set of \"ensembles\" with a coresponding ensemble communicator.\n   * This allows us to run similar, related hyperbolic systems in parallel\n   * on subranges of the set of (global) MPI processes.\n   *\n   * After @p prepare() is called, all getter functions return valid\n   * references.\n   *\n   * @ingroup Miscellaneous\n   */\n  class MPIEnsemble final\n  {\n  public:\n    /**\n     * Prepare the MPI ensemble and split the gobal MPI communicator into\n     * @p n_ensembles different subranges of comparable size. The boolean\n     * @p global_synchronization indicates whether (global) world\n     * synchronization of time step size and other synchronization state is\n     * performend in the HyperbolicModule and ParabolicModule, or whether\n     * such synchronization remains local to the ensemble.\n     *\n     * @pre The total number of mpi ranks must be an integer multiple of\n     * n_ensembles.\n     */\n    MPIEnsemble(const MPI_Comm &mpi_communicator,\n                const int n_ensembles = 1,\n                const bool global_synchronization = true);\n\n    ~MPIEnsemble();\n\n    /**\n     * Return the world communicator.\n     */\n    ACCESSOR_READ_ONLY_NO_DEREFERENCE(world_communicator);\n\n    /**\n     * If true, then ensembles run in lockstep with a synchronized tau_max.\n     */\n    ACCESSOR_READ_ONLY(global_synchronization);\n\n    /**\n     * The (global) world rank of the current MPI process.\n     */\n    ACCESSOR_READ_ONLY(world_rank);\n\n    /**\n     * The total number of (global) MPI processes.\n     */\n    ACCESSOR_READ_ONLY(n_world_ranks);\n\n    /**\n     * Return the ensemble in the interval [0, n_ensembles) that the given\n     * MPI process belongs to.\n     */\n    ACCESSOR_READ_ONLY(ensemble);\n\n    /**\n     * Return the total number of ensembles.\n     */\n    ACCESSOR_READ_ONLY(n_ensembles);\n\n    /**\n     * The (local) ensemble rank of the current MPI process.\n     */\n    ACCESSOR_READ_ONLY(ensemble_rank);\n\n    /**\n     * The total number of (local) MPI processes belonging to the ensemble.\n     */\n    ACCESSOR_READ_ONLY(n_ensemble_ranks);\n\n    /**\n     * The corresponding subrange communicator of the ensemble. The\n     * communicator is collective over all ranks participating in the\n     * ensemble. I.e., it allows for ensemble-local communication.\n     */\n    ACCESSOR_READ_ONLY_NO_DEREFERENCE(ensemble_communicator);\n\n    /**\n     * Return a communicator for synchronization. The method either returns\n     * the (global) world communicator if global synchronization is\n     * enabled, or the (local) ensemble communicator.\n     */\n    DEAL_II_ALWAYS_INLINE inline const MPI_Comm &\n    synchronization_communicator() const\n    {\n      if (global_synchronization_)\n        return world_communicator_;\n      else\n        return ensemble_communicator_;\n    }\n\n    /**\n     * A communicator spanning over all ensemble leaders that have ensemble\n     * rank 0. This communicator is collective over all ensemble leaders\n     * and invalid for all other ranks.\n     */\n    ACCESSOR_READ_ONLY_NO_DEREFERENCE(ensemble_leader_communicator);\n\n    /**\n     * A \"peer communicator\" that groups all kth ranks of each ensemble\n     * together. (Suppose the ensemble_communicator() groups rows, then the\n     * peer_communicator() groups columns of the ensemble partition). The\n     * peer communicator is collective over all world ranks.\n     */\n    ACCESSOR_READ_ONLY_NO_DEREFERENCE(peer_communicator);\n\n  private:\n    const MPI_Comm &world_communicator_;\n\n    bool global_synchronization_;\n\n    int world_rank_;\n    int n_world_ranks_;\n    int ensemble_;\n    int n_ensembles_;\n    int ensemble_rank_;\n    int n_ensemble_ranks_;\n\n    MPI_Group world_group_;\n    std::vector<MPI_Group> ensemble_groups_;\n    MPI_Group ensemble_leader_group_;\n\n    MPI_Comm ensemble_communicator_;\n    MPI_Comm ensemble_leader_communicator_;\n    MPI_Comm peer_communicator_;\n  };\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/mpi_ensemble_container.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"mpi_ensemble.h\"\n\n#include <deal.II/base/mpi.h>\n#include <deal.II/base/utilities.h>\n\nnamespace ryujin\n{\n  /**\n   * A specialized wrapper container used in the TimeLoop that helps\n   * creating instances of HyperbolicSystem / ParabolicSystem /\n   * InitialValues that are MPIEnsemble \"aware:\"\n   *  - For regular equation descriptions we populate a vector with a\n   *    per-ensemble instance of the class template T. The class template T\n   *    must have a constructor that takes a std::string \"subsection\" as\n   *    last argument. We modify this subsection string by appending\n   *    \"/ensemble n\" to the n-th ensemble instance, which then allows to\n   *    configure each ensemble instance individually in the parameter\n   *    file.\n   *\n   *  @fixme For HyperbolicSystem/ ParabolicSystem / InitialValues\n   *  realizations that are already MPIEnsemble \"aware\" we should fall back\n   *  to only creating one instance - possibly by checking for and calling\n   *  a modified constructor that takes an mpi_ensemble as an argument.\n   */\n  template <typename T>\n  class MPIEnsembleContainer\n  {\n  public:\n    /**\n     * Create an independent instance of T for every ensemble of the MPI\n     * ensemble collection. The subsection string for the ParameterAcceptor\n     * is appended by \"/ensemble n\".\n     */\n    template <typename... Args>\n    MPIEnsembleContainer(const MPIEnsemble &mpi_ensemble,\n                         const std::string &subsection,\n                         Args &&...args)\n    {\n      const auto &ensemble = mpi_ensemble.ensemble();\n      const auto &n_ensembles = mpi_ensemble.n_ensembles();\n      unsigned int digits = dealii::Utilities::needed_digits(n_ensembles - 1);\n\n      payload_.resize(n_ensembles);\n      for (int n = 0; n < n_ensembles; ++n) {\n        /* Only append \"/ensemble n\" if we have more than one ensemble: */\n        auto modified = subsection;\n        if (n_ensembles > 1)\n          modified +=\n              \"/ensemble \" + dealii::Utilities::int_to_string(n, digits);\n        payload_[n] =\n            std::make_unique<T>(std::forward<Args>(args)..., modified);\n      }\n\n      ensemble_payload_ = payload_[ensemble].get();\n    }\n\n    /**\n     * Return a const reference to the appropriate class instance for the\n     * current MPI ensemble.\n     */\n    const T &get() const\n    {\n      return *ensemble_payload_;\n    }\n\n    /**\n     * Conversion operator that returns the appropriate class instance for\n     * the current MPI ensemble.\n     */\n    operator const T &() const\n    {\n      return *ensemble_payload_;\n    }\n\n  private:\n    std::vector<std::unique_ptr<T>> payload_;\n    T *ensemble_payload_;\n  };\n\n\n  /**\n   * A trait class that determines whether a given equation Description\n   * struct contains a method or field \"n_mpi_ensembles\".\n   */\n  namespace\n  {\n    template <typename C>\n    static auto test(...) -> std::false_type;\n\n    template <typename C>\n    static auto test(int) -> decltype(C::n_mpi_ensembles(), std::true_type());\n  } // namespace\n\n  template <typename T>\n  constexpr bool has_n_mpi_ensembles_v = decltype(test<T>(0))::value;\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/multicomponent_vector.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2022 - 2024 by the ryujin authors\n//\n\n#include \"multicomponent_vector.h\"\n\nnamespace ryujin\n{\n  namespace Vectors\n  {\n    std::shared_ptr<const dealii::Utilities::MPI::Partitioner>\n    create_vector_partitioner(\n        const std::shared_ptr<const dealii::Utilities::MPI::Partitioner>\n            &scalar_partitioner,\n        const unsigned int n_components)\n    {\n      dealii::IndexSet vector_owned_set(n_components *\n                                        scalar_partitioner->size());\n      for (auto it =\n               scalar_partitioner->locally_owned_range().begin_intervals();\n           it != scalar_partitioner->locally_owned_range().end_intervals();\n           ++it)\n        vector_owned_set.add_range(*it->begin() * n_components,\n                                   (it->last() + 1) * n_components);\n      vector_owned_set.compress();\n      dealii::IndexSet vector_ghost_set(n_components *\n                                        scalar_partitioner->size());\n      for (auto it = scalar_partitioner->ghost_indices().begin_intervals();\n           it != scalar_partitioner->ghost_indices().end_intervals();\n           ++it)\n        vector_ghost_set.add_range(*it->begin() * n_components,\n                                   (it->last() + 1) * n_components);\n      vector_ghost_set.compress();\n      const auto vector_partitioner =\n          std::make_shared<const dealii::Utilities::MPI::Partitioner>(\n              vector_owned_set,\n              vector_ghost_set,\n              scalar_partitioner->get_mpi_communicator());\n\n      return vector_partitioner;\n    }\n  } // namespace Vectors\n} // namespace ryujin\n"
  },
  {
    "path": "source/multicomponent_vector.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <deal.II/base/mpi.h>\n#include <deal.II/base/partitioner.h>\n#include <deal.II/base/vectorization.h>\n#include <deal.II/lac/la_parallel_vector.h>\n\nnamespace ryujin\n{\n  namespace Vectors\n  {\n    /**\n     * This function takes a scalar MPI partitioner @p scalar_partitioner as\n     * argument and returns a shared pointer to a new \"vector\" multicomponent\n     * partitioner that defines storage and MPI synchronization for a vector\n     * consisting of @p n_comp components. The vector partitioner is\n     * intended to efficiently store non-scalar vectors such as the state\n     * vectors U. Let (U_i)_k denote the k-th component of a state vector\n     * element U_i, we then store\n     * \\f{align}\n     *  (U_0)_0, (U_0)_1, (U_0)_2, (U_0)_3, (U_0)_4,\n     *  (U_1)_0, (U_1)_1, (U_1)_2, (U_1)_3, (U_1)_4,\n     *  \\ldots\n     * \\f}\n     *\n     * @note This function is used to efficiently set up a single vector\n     * partitioner in OfflineData used in all MultiComponentVector instances.\n     *\n     * @ingroup SIMD\n     */\n    std::shared_ptr<const dealii::Utilities::MPI::Partitioner>\n    create_vector_partitioner(\n        const std::shared_ptr<const dealii::Utilities::MPI::Partitioner>\n            &scalar_partitioner,\n        const unsigned int n_comp);\n\n\n    template <typename Number,\n              int n_comp,\n              int simd_length = dealii::VectorizedArray<Number>::size(),\n              typename MemorySpace = dealii::MemorySpace::Host,\n              bool writable = true>\n    class MultiComponentVectorView;\n\n\n    /**\n     * A wrapper around dealii::LinearAlgebra::distributed::Vector<Number>\n     * that stores a vector element of @p n_comp components per entry\n     * (instead of a scalar value).\n     *\n     * @ingroup SIMD\n     */\n    template <typename Number,\n              int n_comp,\n              int simd_length = dealii::VectorizedArray<Number>::size()>\n    class MultiComponentVector\n        : public MultiComponentVectorView<Number, n_comp, simd_length>\n    {\n    public:\n      /**\n       * @name Constructor, reinitialization, assignment\n       */\n      //@{\n\n      MultiComponentVector() = default;\n\n      MultiComponentVector(const MultiComponentVector &other);\n\n      MultiComponentVector(MultiComponentVector &&other) noexcept;\n\n      /**\n       * Reinitializes the MultiComponentVector with a vector MPI partitioner\n       * that was created first with create_vector_partitioner().\n       */\n      void reinit_with_vector_partitioner(\n          const std::shared_ptr<const dealii::Utilities::MPI::Partitioner>\n              &vector_partitioner);\n\n      /**\n       * Reinitializes the MultiComponentVector with a scalar MPI partitioner.\n       * The function calls create_vector_partitioner() internally to\n       * create and store a corresponding \"vector\" MPI partitioner.\n       */\n      void reinit_with_scalar_partitioner(\n          const std::shared_ptr<const dealii::Utilities::MPI::Partitioner>\n              &scalar_partitioner);\n\n      MultiComponentVector &operator=(const MultiComponentVector &other);\n\n      MultiComponentVector &operator=(MultiComponentVector &&other) noexcept;\n\n      //@}\n      /**\n       * Memory space access and synchronization:\n       */\n      //@{\n\n      /**\n       * Return a writable view on the sparse matrix for the selected memory\n       * space.\n       */\n      template <typename MemorySpace = dealii::MemorySpace::Host>\n      MultiComponentVectorView<Number, n_comp, simd_length, MemorySpace, true>\n      get_view();\n\n      /**\n       * Return a read-only view on the sparse matrix for the selected memory\n       * space.\n       */\n      template <typename MemorySpace = dealii::MemorySpace::Host>\n      MultiComponentVectorView<Number, n_comp, simd_length, MemorySpace, false>\n      get_view() const;\n\n      /**\n       * Returns true if the templated memory space is the currently active\n       * memory space.\n       */\n      template <typename MemorySpace>\n      bool is_active_memory_space() const;\n\n      /**\n       * Move internal data from the currently active memory space to the\n       * templated memory space.\n       */\n      template <typename MemorySpace>\n      void move_to_memory_space();\n\n      //@}\n\n      //@}\n      /**\n       * MPI synchronization.\n       */\n      //@{\n\n      /**\n       * MPI synchronization: Zero out all ghost rows.\n       */\n      template <typename MemorySpace>\n      void zero_out_ghost_values_on_memory_space();\n\n      /**\n       * MPI synchronization: Import all ghost values from neighboring MPI\n       * ranks on the templated memory space.\n       */\n      template <typename MemorySpace>\n      void update_ghost_values_on_memory_space();\n\n      /**\n       * MPI synchronization: Copy the data that has accumulated in the\n       * ghost range to the owning processor. This function operates on the\n       * templated memory space.\n       */\n      template <typename MemorySpace>\n      void compress_on_memory_space(dealii::VectorOperation::values operation);\n\n    private:\n      //@}\n      /**\n       * @name Internal fields, methods, and friends\n       */\n      //@{\n\n      dealii::LinearAlgebra::distributed::Vector<Number,\n                                                 dealii::MemorySpace::Host>\n          host_vector_;\n\n      /*\n       * We avoid setting up the default_vector_ if default and host happen\n       * to be the same memory space.\n       */\n      static constexpr bool have_separate_memory_spaces =\n          !std::is_same_v<dealii::MemorySpace::Host::kokkos_space,\n                          dealii::MemorySpace::Default::kokkos_space>;\n\n      dealii::LinearAlgebra::distributed::Vector<Number,\n                                                 dealii::MemorySpace::Default>\n          default_vector_;\n\n      bool host_space_active_ = true;\n\n      template <typename, int, int, typename, bool>\n      friend class MultiComponentVectorView;\n\n      //@}\n    };\n\n\n    template <typename Number,\n              int n_comp,\n              int simd_length,\n              typename MemorySpace,\n              bool writable>\n    class MultiComponentVectorView\n    {\n    public:\n      /**\n       * @name Constructor and reinitialization\n       */\n      //@{\n\n      MultiComponentVectorView() = default;\n\n      MultiComponentVectorView(MultiComponentVector<Number, n_comp, simd_length>\n                                   &multi_component_vector)\n        requires(writable);\n\n      MultiComponentVectorView(\n          const MultiComponentVector<Number, n_comp, simd_length>\n              &multi_component_vector)\n        requires(!writable);\n\n      template <typename MultiComponentVector>\n      void reinit(MultiComponentVector &multi_component_vector)\n        requires(writable != std::is_const_v<MultiComponentVector>);\n\n      //@}\n      /**\n       * @name Typedefs and constexpr constants\n       */\n      //@{\n\n      /**\n       * Shorthand typedef for the underlying scalar\n       * dealii::LinearAlgebra::distributed::Vector<Number, MemorySpace> used to\n       * insert and extract a single component of the MultiComponentVector.\n       */\n      using ScalarVector =\n          dealii::LinearAlgebra::distributed::Vector<Number, MemorySpace>;\n\n      //@}\n      /**\n       * @name Extracting and inserting components, scaled addition\n       */\n      //@{\n\n      /**\n       * Extracts a single component out of the MultiComponentVector and\n       * stores it in @p scalar_vector. The destination vector must have a\n       * compatible corresponding (scalar) MPI partitioner, i.e., the \"local\n       * size\", the number of locally owned elements, has to match.\n       *\n       * The function calls scalar_vector.update_ghost_values() before\n       * returning.\n       *\n       * Optionally, a third argument @p functor can be supplied that is\n       * applied to each (scalar) value individually before stored in\n       * @p scalar_vector.\n       *\n       * @note This function is used in the VTUOutput module to unpack a\n       * single component out of our custom MultiComponentVector in order to\n       * call deal.II specific functions (that can only operate on scalar\n       * vectors).\n       */\n      template <typename Functor = std::identity>\n      void extract_component(ScalarVector &scalar_vector,\n                             unsigned int component,\n                             const Functor &functor = std::identity{}) const;\n\n      /**\n       * Inserts a single component into a MultiComponentVector. The source\n       * vector must have a compatible corresponding (scalar) MPI\n       * partitioner, i.e., the \"local size\", the number of locally owned\n       * elements, has to match.\n       *\n       * The function does not call update_ghost_values() automatically. This\n       * has to be done by the user once all components are updated.\n       *\n       * Optionally, a third argument @p functor can be supplied that is\n       * applied to each (scalar) value individually before stored in the\n       * corresponding component.\n       *\n       * @note This function is used in InitialValues to populate all\n       * components of the initial state that are returned component wise as\n       * single scalar vectors by deal.II interpolation functions.\n       */\n      template <typename Functor = std::identity>\n      void insert_component(const ScalarVector &scalar_vector,\n                            unsigned int component,\n                            const Functor &functor = std::identity{}) const\n        requires writable;\n\n      /**\n       * Variant of the method above that reads values out of a\n       * dealii::Vector in (rank-) local numbering.\n       */\n      template <typename Functor = std::identity>\n      void insert_component(const dealii::Vector<Number> &scalar_vector,\n                            unsigned int component,\n                            const Functor &functor = std::identity{}) const\n        requires writable;\n\n      /**\n       * Scaled addition of the given vector $U$ and the argument vector\n       * $V$: $U\\leftarrow s\\,U+a\\,V$.\n       */\n      void sadd(const Number s,\n                const Number a,\n                const MultiComponentVectorView &v) const\n        requires writable;\n\n      //@}\n      /**\n       * @name Access to scalar or tensor-valued entries from various\n       * contexts (scalar, SIMD, GPU):\n       */\n      //@{\n\n      /**\n       * Return the entry indexed by @p i.\n       *\n       * If the template parameter @a Number2 is a VectorizedArray then\n       * the function returns a SIMD vectorized dealii::Tensor populated with\n       * entries from the @p n_comp component vectors stored at\n       * indices i, i+1, ..., i+simd_length-1.\n       *\n       * @note This function is only available if `n_comp` is equal to 1.\n       */\n      template <typename Number2 = Number>\n      DEAL_II_HOST_DEVICE Number2 read_entry(const unsigned int i) const;\n\n      /**\n       * Variant of above function.\n       *\n       * Returns a SIMD vectorized dealii::Tensor populated with entries from\n       * the @p n_comp component vectors stored at indices *(js), *(js+1),\n       * ..., *(js+simd_length-1), i.e., @p js has to point to an array of\n       * size @p simd_length containing all indices.\n       *\n       * @note This function is only available if `n_comp` is equal to 1.\n       */\n      template <typename Number2 = Number>\n      DEAL_II_HOST_DEVICE Number2 read_entry(const unsigned int *js) const;\n\n      /**\n       * Return the tensor-valued entry indexed by @p i.\n       *\n       * If the template parameter @a Number2 is a VectorizedArray then\n       * the function returns a SIMD vectorized dealii::Tensor populated with\n       * entries from the @p n_compontens component vectors stored at\n       * indices i, i+1, ..., i+simd_length-1.\n       */\n      template <typename Number2 = Number,\n                typename Tensor = dealii::Tensor<1, n_comp, Number2>>\n      DEAL_II_HOST_DEVICE Tensor read_tensor(const unsigned int i) const;\n\n      /**\n       * Variant of above function.\n       *\n       * Returns a SIMD vectorized dealii::Tensor populated with entries from\n       * the @p n_comp component vectors stored at indices *(js), *(js+1),\n       * ..., *(js+simd_length-1), i.e., @p js has to point to an array of\n       * size @p simd_length containing all indices.\n       */\n      template <typename Number2 = Number,\n                typename Tensor = dealii::Tensor<1, n_comp, Number2>>\n      DEAL_II_HOST_DEVICE Tensor read_tensor(const unsigned int *js) const;\n\n      /**\n       * Write a (scalar valued) @p entry to the vector at position by @p i.\n       *\n       * If the template parameter @a Number2 is a VectorizedArray then\n       * the function takes a SIMD vectorized @p tensor as argument instead\n       * and updates the values of the @p n_comp component vectors at\n       * indices i, i+1, ..., i+simd_length_1. with the values supplied by @p\n       * tensor.\n       *\n       * @note This function is only available if `n_comp` is equal to 1.\n       */\n      template <typename Number2 = Number>\n      DEAL_II_HOST_DEVICE void write_entry(const Number2 &entry,\n                                           const unsigned int i) const\n        requires writable;\n\n      /**\n       * Update the values of the @p n_comp component vector at index\n       * @p i with the values supplied by @p tensor.\n       *\n       * If the template parameter @a Number2 is a VectorizedArray then\n       * the function takes a SIMD vectorized @p tensor as argument instead\n       * and updates the values of the @p n_comp component vectors at\n       * indices i, i+1, ..., i+simd_length_1. with the values supplied by @p\n       * tensor.\n       *\n       * @note @p tensor can be an arbitrary indexable container, such as\n       * dealii::Tensor or std::array, that has an `operator[]()` returning a @p\n       * Number, and has a type trait `value_type`.\n       */\n      template <typename Number2 = Number,\n                typename Tensor = dealii::Tensor<1, n_comp, Number2>>\n      DEAL_II_HOST_DEVICE void write_tensor(const Tensor &tensor,\n                                            const unsigned int i) const\n        requires writable;\n\n      /**\n       * Add a (scalar valued) @p entry to the vector at position @p i.\n       * Update the values of the @p n_comp component vector at index @p i\n       * by adding the values supplied by @p tensor.\n       *\n       * If the template parameter @a Number2 is a VectorizedArray then\n       * the function takes a SIMD vectorized @p tensor as argument instead\n       * and updates the values of the @p n_comp component vectors at\n       * indices i, i+1, ..., i+simd_length_1. with the values supplied by @p\n       * tensor.\n       *\n       * @note This function is only available if `n_comp` is equal to 1.\n       */\n\n      template <typename Number2 = Number>\n      DEAL_II_HOST_DEVICE void add_entry(const Number2 &entry,\n                                         const unsigned int i) const\n        requires writable;\n\n      /**\n       * Update the values of the @p n_comp component vector at index @p i\n       * by adding the values supplied by @p tensor.\n       *\n       * If the template parameter @a Number2 is a VectorizedArray then\n       * the function takes a SIMD vectorized @p tensor as argument instead\n       * and updates the values of the @p n_comp component vectors at\n       * indices i, i+1, ..., i+simd_length_1. with the values supplied by @p\n       * tensor.\n       *\n       * @note @p tensor can be an arbitrary indexable container, such as\n       * dealii::Tensor or std::array, that has an `operator[]()` returning a @p\n       * Number, and has a type trait `value_type`.\n       */\n      template <typename Number2 = Number,\n                typename Tensor = dealii::Tensor<1, n_comp, Number2>>\n      DEAL_II_HOST_DEVICE void add_tensor(const Tensor &tensor,\n                                          const unsigned int i) const\n        requires writable;\n\n      //@}\n      /**\n       * MPI synchronization.\n       */\n      //@{\n\n      /**\n       * MPI synchronization: Zero out all ghost values stored in the vector.\n       */\n      void zero_out_ghost_values() const\n        requires(writable);\n\n      /**\n       * MPI synchronization: Import all ghost values from neighboring MPI\n       * ranks on the templated memory space.\n       */\n      void update_ghost_values() const\n        requires(writable);\n\n      /**\n       * MPI synchronization: Copy the data that has accumulated in the\n       * ghost range to the owning processor. This function operates on the\n       * templated memory space.\n       */\n      void compress(dealii::VectorOperation::values operation) const\n        requires(writable);\n\n    private:\n      //@}\n      /**\n       * Internal data fields:\n       */\n      //@{\n\n      using MCV = MultiComponentVector<Number, n_comp, simd_length>;\n      std::conditional_t<writable, MCV *, const MCV *> multi_component_vector_;\n\n      Number *data_;\n      unsigned int n_locally_owned_;\n      unsigned int n_locally_relevant_;\n      //@}\n    };\n\n\n#ifndef DOXYGEN\n    /*\n     * -------------------------------------------------------------------------\n     * Inline function definitions\n     * -------------------------------------------------------------------------\n     */\n\n\n    template <typename Number, int n_comp, int simd_length>\n    MultiComponentVector<Number, n_comp, simd_length>::MultiComponentVector(\n        const MultiComponentVector &other)\n        : MultiComponentVectorView<Number, n_comp, simd_length>()\n    {\n      *this = other;\n    }\n\n\n    template <typename Number, int n_comp, int simd_length>\n    MultiComponentVector<Number, n_comp, simd_length>::MultiComponentVector(\n        MultiComponentVector &&other) noexcept\n        : MultiComponentVectorView<Number, n_comp, simd_length>()\n    {\n      *this = other;\n    }\n\n\n    template <typename Number, int n_comp, int simd_length>\n    void MultiComponentVector<Number, n_comp, simd_length>::\n        reinit_with_vector_partitioner(\n            const std::shared_ptr<const dealii::Utilities::MPI::Partitioner>\n                &vector_partitioner)\n    {\n      /* Special case of a zero component vector */\n      if (n_comp == 0)\n        return;\n\n      host_vector_.reinit(vector_partitioner);\n      if constexpr (have_separate_memory_spaces)\n        default_vector_.reinit(vector_partitioner);\n\n      /* Reinitialize view to point to the correct vector data: */\n      MultiComponentVectorView<Number, n_comp, simd_length>::reinit(*this);\n    }\n\n\n    template <typename Number, int n_comp, int simd_length>\n    void MultiComponentVector<Number, n_comp, simd_length>::\n        reinit_with_scalar_partitioner(\n            const std::shared_ptr<const dealii::Utilities::MPI::Partitioner>\n                &scalar_partitioner)\n    {\n      /* Special case of a zero component vector: */\n      if (n_comp == 0)\n        return;\n\n      /* Special case of a scalar vector: */\n      if (n_comp == 1)\n        host_vector_.reinit(scalar_partitioner);\n\n      auto vector_partitioner =\n          create_vector_partitioner(scalar_partitioner, n_comp);\n\n      host_vector_.reinit(vector_partitioner);\n      if constexpr (have_separate_memory_spaces)\n        default_vector_.reinit(vector_partitioner);\n\n      /* Reinitialize view to point to the correct vector data: */\n      MultiComponentVectorView<Number, n_comp, simd_length>::reinit(*this);\n    }\n\n\n    template <typename Number, int n_comp, int simd_length>\n    auto MultiComponentVector<Number, n_comp, simd_length>::operator=(\n        const MultiComponentVector &other) -> MultiComponentVector &\n    {\n      host_vector_ = other.host_vector_;\n      if constexpr (have_separate_memory_spaces)\n        default_vector_ = other.default_vector_;\n      host_space_active_ = other.host_space_active_;\n\n      /* Reinitialize view to point to the correct vector data: */\n      MultiComponentVectorView<Number, n_comp, simd_length>::reinit(*this);\n\n      return *this;\n    }\n\n\n    template <typename Number, int n_comp, int simd_length>\n    auto MultiComponentVector<Number, n_comp, simd_length>::operator=(\n        MultiComponentVector &&other) noexcept -> MultiComponentVector &\n    {\n      host_vector_ = std::move(other.host_vector_);\n      if constexpr (have_separate_memory_spaces)\n        default_vector_ = std::move(other.default_vector_);\n      host_space_active_ = other.host_space_active_;\n\n      /* Reinitialize view to point to the correct vector data: */\n      MultiComponentVectorView<Number, n_comp, simd_length>::reinit(*this);\n\n      return *this;\n    }\n\n\n    template <typename Number, int n_comp, int simd_length>\n    template <typename MemorySpace>\n    MultiComponentVectorView<Number, n_comp, simd_length, MemorySpace, true>\n    MultiComponentVector<Number, n_comp, simd_length>::get_view()\n    {\n      Assert(is_active_memory_space<MemorySpace>(),\n             dealii::ExcMessage(\"The chosen memory space is not active.\"));\n\n      return MultiComponentVectorView<Number,\n                                      n_comp,\n                                      simd_length,\n                                      MemorySpace,\n                                      true>(*this);\n    }\n\n\n    template <typename Number, int n_comp, int simd_length>\n    template <typename MemorySpace>\n    MultiComponentVectorView<Number, n_comp, simd_length, MemorySpace, false>\n    MultiComponentVector<Number, n_comp, simd_length>::get_view() const\n    {\n      Assert(is_active_memory_space<MemorySpace>(),\n             dealii::ExcMessage(\"The chosen memory space is not active.\"));\n\n      return MultiComponentVectorView<Number,\n                                      n_comp,\n                                      simd_length,\n                                      MemorySpace,\n                                      false>(*this);\n    }\n\n\n    template <typename Number, int n_comp, int simd_length>\n    template <typename MemorySpace>\n    bool\n    MultiComponentVector<Number, n_comp, simd_length>::is_active_memory_space()\n        const\n    {\n      using HostSpace = dealii::MemorySpace::Host;\n      using DefaultSpace = dealii::MemorySpace::Default;\n      static_assert(std::is_same_v<MemorySpace, HostSpace> ||\n                        std::is_same_v<MemorySpace, DefaultSpace>,\n                    \"Unexpected memory space\");\n\n      return host_space_active_ == std::is_same_v<MemorySpace, HostSpace>;\n    }\n\n\n    template <typename Number, int n_comp, int simd_length>\n    template <typename MemorySpace>\n    void\n    MultiComponentVector<Number, n_comp, simd_length>::move_to_memory_space()\n    {\n      using HostSpace = dealii::MemorySpace::Host;\n      using DefaultSpace = dealii::MemorySpace::Default;\n      static_assert(std::is_same_v<MemorySpace, HostSpace> ||\n                        std::is_same_v<MemorySpace, DefaultSpace>,\n                    \"Unexpected memory space\");\n\n      if (is_active_memory_space<MemorySpace>())\n        return;\n\n      if constexpr (std::is_same_v<MemorySpace, HostSpace>) {\n        if constexpr (have_separate_memory_spaces)\n          host_vector_.import_elements(default_vector_,\n                                       dealii::VectorOperation::insert);\n        host_space_active_ = true;\n\n      } else if constexpr (std::is_same_v<MemorySpace, DefaultSpace>) {\n        if constexpr (have_separate_memory_spaces)\n          default_vector_.import_elements(host_vector_,\n                                          dealii::VectorOperation::insert);\n        host_space_active_ = false;\n      }\n    }\n\n\n    template <typename Number, int n_components, int simd_length>\n    template <typename MemorySpace>\n    void MultiComponentVector<Number, n_components, simd_length>::\n        zero_out_ghost_values_on_memory_space()\n    {\n      using HostSpace = dealii::MemorySpace::Host;\n      using DefaultSpace = dealii::MemorySpace::Default;\n      static_assert(std::is_same_v<MemorySpace, HostSpace> ||\n                        std::is_same_v<MemorySpace, DefaultSpace>,\n                    \"Unexpected memory space\");\n\n      if constexpr (have_separate_memory_spaces &&\n                    !std::is_same_v<MemorySpace, HostSpace>) {\n        default_vector_.zero_out_ghost_values();\n      } else {\n        host_vector_.zero_out_ghost_values();\n      }\n    }\n\n\n    template <typename Number, int n_components, int simd_length>\n    template <typename MemorySpace>\n    void MultiComponentVector<Number, n_components, simd_length>::\n        update_ghost_values_on_memory_space()\n    {\n      using HostSpace = dealii::MemorySpace::Host;\n      using DefaultSpace = dealii::MemorySpace::Default;\n      static_assert(std::is_same_v<MemorySpace, HostSpace> ||\n                        std::is_same_v<MemorySpace, DefaultSpace>,\n                    \"Unexpected memory space\");\n\n      if constexpr (have_separate_memory_spaces &&\n                    !std::is_same_v<MemorySpace, HostSpace>) {\n        default_vector_.update_ghost_values();\n      } else {\n        host_vector_.update_ghost_values();\n      }\n    }\n\n\n    template <typename Number, int n_components, int simd_length>\n    template <typename MemorySpace>\n    void MultiComponentVector<Number, n_components, simd_length>::\n        compress_on_memory_space(dealii::VectorOperation::values operation)\n    {\n      using HostSpace = dealii::MemorySpace::Host;\n      using DefaultSpace = dealii::MemorySpace::Default;\n      static_assert(std::is_same_v<MemorySpace, HostSpace> ||\n                        std::is_same_v<MemorySpace, DefaultSpace>,\n                    \"Unexpected memory space\");\n\n      if constexpr (have_separate_memory_spaces &&\n                    !std::is_same_v<MemorySpace, HostSpace>) {\n        default_vector_.compress(operation);\n      } else {\n        host_vector_.compress(operation);\n      }\n    }\n\n\n    template <typename Number,\n              int n_comp,\n              int simd_l,\n              typename MemorySpace,\n              bool writable>\n    MultiComponentVectorView<Number, n_comp, simd_l, MemorySpace, writable>::\n        MultiComponentVectorView(MultiComponentVector<Number, n_comp, simd_l>\n                                     &multi_component_vector)\n      requires(writable)\n    {\n      reinit(multi_component_vector);\n    }\n\n\n    template <typename Number,\n              int n_comp,\n              int simd_l,\n              typename MemorySpace,\n              bool writable>\n    MultiComponentVectorView<Number, n_comp, simd_l, MemorySpace, writable>::\n        MultiComponentVectorView(\n            const MultiComponentVector<Number, n_comp, simd_l>\n                &multi_component_vector)\n      requires(!writable)\n    {\n      reinit(multi_component_vector);\n    }\n\n\n    template <typename Number,\n              int n_comp,\n              int simd_l,\n              typename MemorySpace,\n              bool writable>\n    template <typename MultiComponentVector>\n    void\n    MultiComponentVectorView<Number, n_comp, simd_l, MemorySpace, writable>::\n        reinit(MultiComponentVector &multi_component_vector)\n      requires(writable != std::is_const_v<MultiComponentVector>)\n    {\n      using HostSpace = dealii::MemorySpace::Host;\n      using DefaultSpace = dealii::MemorySpace::Default;\n      static_assert(std::is_same_v<MemorySpace, HostSpace> ||\n                        std::is_same_v<MemorySpace, DefaultSpace>,\n                    \"Unexpected memory space\");\n\n      multi_component_vector_ = &multi_component_vector;\n\n      constexpr bool have_separate_memory_spaces =\n          ::ryujin::Vectors::MultiComponentVector<Number, n_comp, simd_l>::\n              have_separate_memory_spaces;\n\n      if constexpr (have_separate_memory_spaces &&\n                    !std::is_same_v<MemorySpace, HostSpace>) {\n        auto &vector = multi_component_vector_->default_vector_;\n        const auto &partitioner = vector.get_partitioner();\n\n        data_ = vector.begin();\n        n_locally_owned_ = partitioner->locally_owned_size();\n        n_locally_relevant_ = n_locally_owned_ + partitioner->n_ghost_indices();\n\n      } else {\n        auto &vector = multi_component_vector_->host_vector_;\n        const auto &partitioner = vector.get_partitioner();\n\n        data_ = vector.begin();\n        n_locally_owned_ = partitioner->locally_owned_size();\n        n_locally_relevant_ = n_locally_owned_ + partitioner->n_ghost_indices();\n      }\n    }\n\n\n    template <typename Number,\n              int n_comp,\n              int simd_length,\n              typename MemorySpace,\n              bool writable>\n    template <typename Functor>\n    void MultiComponentVectorView<\n        Number,\n        n_comp,\n        simd_length,\n        MemorySpace,\n        writable>::extract_component(ScalarVector &scalar_vector,\n                                     unsigned int component,\n                                     const Functor &functor) const\n    {\n      using HostSpace = dealii::MemorySpace::Host;\n      AssertThrow((std::is_same_v<MemorySpace, HostSpace>),\n                  dealii::ExcNotImplemented());\n\n      Assert(n_comp > 0,\n             dealii::ExcMessage(\n                 \"Cannot extract from a vector with zero components.\"));\n      AssertIndexRange(component, n_comp);\n\n      const auto local_size =\n          scalar_vector.get_partitioner()->locally_owned_size();\n\n      Assert(n_comp * local_size == n_locally_owned_,\n             dealii::ExcMessage(\"Called with a scalar_vector argument that has \"\n                                \"incompatible local range.\"));\n\n      for (unsigned int i = 0; i < local_size; ++i)\n        scalar_vector.local_element(i) = functor(data_[i * n_comp + component]);\n      scalar_vector.update_ghost_values();\n    }\n\n\n    template <typename Number,\n              int n_comp,\n              int simd_length,\n              typename MemorySpace,\n              bool writable>\n    template <typename Functor>\n    void MultiComponentVectorView<\n        Number,\n        n_comp,\n        simd_length,\n        MemorySpace,\n        writable>::insert_component(const ScalarVector &scalar_vector,\n                                    unsigned int component,\n                                    const Functor &functor) const\n      requires writable\n    {\n      using HostSpace = dealii::MemorySpace::Host;\n      AssertThrow((std::is_same_v<MemorySpace, HostSpace>),\n                  dealii::ExcNotImplemented());\n\n      Assert(n_comp > 0,\n             dealii::ExcMessage(\n                 \"Cannot insert into a vector with zero components.\"));\n      AssertIndexRange(component, n_comp);\n\n      const auto local_size =\n          scalar_vector.get_partitioner()->locally_owned_size();\n\n      Assert(n_comp * local_size == n_locally_owned_,\n             dealii::ExcMessage(\"Called with a scalar_vector argument that has \"\n                                \"incompatible local range.\"));\n\n      for (unsigned int i = 0; i < local_size; ++i)\n        data_[i * n_comp + component] = functor(scalar_vector.local_element(i));\n    }\n\n\n    template <typename Number,\n              int n_comp,\n              int simd_length,\n              typename MemorySpace,\n              bool writable>\n    template <typename Functor>\n    void MultiComponentVectorView<\n        Number,\n        n_comp,\n        simd_length,\n        MemorySpace,\n        writable>::insert_component(const dealii::Vector<Number> &scalar_vector,\n                                    unsigned int component,\n                                    const Functor &functor) const\n      requires writable\n    {\n      using HostSpace = dealii::MemorySpace::Host;\n      AssertThrow((std::is_same_v<MemorySpace, HostSpace>),\n                  dealii::ExcInternalError());\n\n      Assert(n_comp > 0,\n             dealii::ExcMessage(\n                 \"Cannot insert into a vector with zero components.\"));\n      AssertIndexRange(component, n_comp);\n\n      const auto local_size = scalar_vector.size();\n\n      Assert(n_comp * local_size >= n_locally_owned_,\n             dealii::ExcMessage(\"Called with a scalar_vector argument that has \"\n                                \"incompatible local range.\"));\n\n      for (unsigned int i = 0; i < local_size; ++i)\n        data_[i * n_comp + component] = functor(scalar_vector[i]);\n    }\n\n\n    template <typename Num, int n_comp, int simd_l, typename MS, bool writable>\n    void MultiComponentVectorView<Num, n_comp, simd_l, MS, writable>::sadd(\n        const Num s, const Num a, const MultiComponentVectorView &v) const\n      requires writable\n    {\n      using HS = dealii::MemorySpace::Host;\n      using DS = dealii::MemorySpace::Default;\n      static_assert(std::is_same_v<MS, HS> || std::is_same_v<MS, DS>,\n                    \"Unexpected memory space\");\n\n      if constexpr (std::is_same_v<MS, HS>) {\n        const auto &other = v.multi_component_vector_->host_vector_;\n        multi_component_vector_->host_vector_.sadd(s, a, other);\n      } else if constexpr (std::is_same_v<MS, DS>) {\n        const auto &other = v.multi_component_vector_->default_vector_;\n        multi_component_vector_->default_vector_.sadd(s, a, other);\n      }\n    }\n\n\n    template <typename Number,\n              int n_comp,\n              int simd_length,\n              typename MemorySpace,\n              bool writable>\n    template <typename Number2>\n    DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number2\n    MultiComponentVectorView<Number,\n                             n_comp,\n                             simd_length,\n                             MemorySpace,\n                             writable>::read_entry(const unsigned int i) const\n    {\n      static_assert(\n          n_comp == 1,\n          \"Attempted to read a scalar value from a tensor-valued vector entry\");\n\n      AssertIndexRange(i, n_locally_relevant_);\n\n      const auto result = read_tensor<Number2>(i);\n      return result[0];\n    }\n\n\n    template <typename Number,\n              int n_comp,\n              int simd_length,\n              typename MemorySpace,\n              bool writable>\n    template <typename Number2, typename Tensor>\n    DEAL_II_HOST_DEVICE_ALWAYS_INLINE Tensor\n    MultiComponentVectorView<Number,\n                             n_comp,\n                             simd_length,\n                             MemorySpace,\n                             writable>::read_tensor(const unsigned int i) const\n    {\n      static_assert(std::is_same_v<Number2, typename Tensor::value_type>,\n                    \"type mismatch\");\n\n      AssertIndexRange(i, n_locally_relevant_);\n\n      Tensor tensor;\n\n      /* Special case of a zero component vector */\n      if constexpr (n_comp == 0)\n        return tensor;\n\n      using VA = dealii::VectorizedArray<Number>;\n      if constexpr (std::is_same_v<VA, Number2>) {\n        /* Vectorized fast access. index must be divisible by simd_length */\n        std::array<unsigned int, VA::size()> indices;\n        for (unsigned int k = 0; k < VA::size(); ++k)\n          indices[k] = k * n_comp;\n\n        dealii::vectorized_load_and_transpose(\n            n_comp, data_ + i * n_comp, indices.data(), &tensor[0]);\n\n      } else {\n        /* Non-vectorized sequential access. */\n        for (unsigned int d = 0; d < n_comp; ++d)\n          tensor[d] = data_[i * n_comp + d];\n      }\n\n      return tensor;\n    }\n\n\n    template <typename Number,\n              int n_comp,\n              int simd_length,\n              typename MemorySpace,\n              bool writable>\n    template <typename Number2>\n    DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number2\n    MultiComponentVectorView<Number,\n                             n_comp,\n                             simd_length,\n                             MemorySpace,\n                             writable>::read_entry(const unsigned int *js) const\n    {\n      static_assert(\n          n_comp == 1,\n          \"Attempted to read a scalar value from a tensor-valued vector entry\");\n\n      const auto result = read_tensor<Number2>(js);\n      return result[0];\n    }\n\n\n    template <typename Number,\n              int n_comp,\n              int simd_length,\n              typename MemorySpace,\n              bool writable>\n    template <typename Number2, typename Tensor>\n    DEAL_II_HOST_DEVICE_ALWAYS_INLINE Tensor\n    MultiComponentVectorView<Number,\n                             n_comp,\n                             simd_length,\n                             MemorySpace,\n                             writable>::read_tensor(const unsigned int *js)\n        const\n    {\n      static_assert(std::is_same_v<Number2, typename Tensor::value_type>,\n                    \"type mismatch\");\n\n      Tensor tensor;\n\n      /* Special case of a zero component vector */\n      if constexpr (n_comp == 0)\n        return tensor;\n\n      using VA = dealii::VectorizedArray<Number>;\n      if constexpr (std::is_same_v<VA, Number2>) {\n        /* Vectorized fast access. index must be divisible by simd_length */\n\n        std::array<unsigned int, VA::size()> indices;\n        for (unsigned int k = 0; k < VA::size(); ++k) {\n          AssertIndexRange(js[k], n_locally_relevant_);\n          indices[k] = js[k] * n_comp;\n        }\n\n        dealii::vectorized_load_and_transpose(\n            n_comp, data_, indices.data(), &tensor[0]);\n\n      } else {\n        /* Non-vectorized sequential access. */\n\n        AssertIndexRange(*js, n_locally_relevant_);\n\n        for (unsigned int d = 0; d < n_comp; ++d)\n          tensor[d] = data_[js[0] * n_comp + d];\n      }\n\n      return tensor;\n    }\n\n\n    template <typename Number,\n              int n_comp,\n              int simd_length,\n              typename MemorySpace,\n              bool writable>\n    template <typename Number2>\n    DEAL_II_HOST_DEVICE_ALWAYS_INLINE void\n    MultiComponentVectorView<Number,\n                             n_comp,\n                             simd_length,\n                             MemorySpace,\n                             writable>::write_entry(const Number2 &entry,\n                                                    const unsigned int i) const\n      requires writable\n    {\n      static_assert(n_comp == 1,\n                    \"Attempted to write a scalar value into a tensor-valued \"\n                    \"vector entry\");\n\n      AssertIndexRange(i, n_locally_relevant_);\n\n      dealii::Tensor<1, n_comp, Number2> tensor;\n      tensor[0] = entry;\n\n      write_tensor<Number2>(tensor, i);\n    }\n\n\n    template <typename Number,\n              int n_comp,\n              int simd_length,\n              typename MemorySpace,\n              bool writable>\n    template <typename Number2, typename Tensor>\n    DEAL_II_HOST_DEVICE_ALWAYS_INLINE void\n    MultiComponentVectorView<Number,\n                             n_comp,\n                             simd_length,\n                             MemorySpace,\n                             writable>::write_tensor(const Tensor &tensor,\n                                                     const unsigned int i) const\n      requires writable\n    {\n      static_assert(std::is_same_v<Number2, typename Tensor::value_type>,\n                    \"type mismatch\");\n\n      AssertIndexRange(i, n_locally_relevant_);\n\n      /* Special case of a zero component vector */\n      if constexpr (n_comp == 0)\n        return;\n\n      using VA = dealii::VectorizedArray<Number>;\n      if constexpr (std::is_same_v<VA, Number2>) {\n        /* Vectorized fast access. index must be divisible by simd_length */\n\n        std::array<unsigned int, VA::size()> indices;\n        for (unsigned int k = 0; k < VA::size(); ++k)\n          indices[k] = k * n_comp;\n\n        dealii::vectorized_transpose_and_store(/*add into*/ false,\n                                               n_comp,\n                                               &tensor[0],\n                                               indices.data(),\n                                               data_ + i * n_comp);\n\n      } else {\n        /* Non-vectorized sequential access. */\n\n        for (unsigned int d = 0; d < n_comp; ++d)\n          data_[i * n_comp + d] = tensor[d];\n      }\n    }\n\n\n    template <typename Number,\n              int n_comp,\n              int simd_length,\n              typename MemorySpace,\n              bool writable>\n    template <typename Number2>\n    DEAL_II_HOST_DEVICE_ALWAYS_INLINE void\n    MultiComponentVectorView<Number,\n                             n_comp,\n                             simd_length,\n                             MemorySpace,\n                             writable>::add_entry(const Number2 &entry,\n                                                  const unsigned int i) const\n      requires writable\n    {\n      static_assert(n_comp == 1,\n                    \"Attempted to write a scalar value into a tensor-valued \"\n                    \"matrix entry\");\n\n      AssertIndexRange(i, n_locally_relevant_);\n\n      dealii::Tensor<1, n_comp, Number2> tensor;\n      tensor[0] = entry;\n\n      add_tensor<Number2>(tensor, i);\n    }\n\n\n    template <typename Number,\n              int n_comp,\n              int simd_length,\n              typename MemorySpace,\n              bool writable>\n    template <typename Number2, typename Tensor>\n    DEAL_II_HOST_DEVICE_ALWAYS_INLINE void\n    MultiComponentVectorView<Number,\n                             n_comp,\n                             simd_length,\n                             MemorySpace,\n                             writable>::add_tensor(const Tensor &tensor,\n                                                   const unsigned int i) const\n      requires writable\n    {\n      static_assert(std::is_same_v<Number2, typename Tensor::value_type>,\n                    \"type mismatch\");\n\n      AssertIndexRange(i, n_locally_relevant_);\n\n      /* Special case of a zero component vector */\n      if constexpr (n_comp == 0)\n        return;\n\n      using VA = dealii::VectorizedArray<Number>;\n      if constexpr (std::is_same_v<VA, Number2>) {\n        /* Vectorized fast access. index must be divisible by simd_length */\n\n        std::array<unsigned int, VA::size()> indices;\n        for (unsigned int k = 0; k < VA::size(); ++k)\n          indices[k] = k * n_comp;\n\n        dealii::vectorized_transpose_and_store(/*add into*/ true,\n                                               n_comp,\n                                               &tensor[0],\n                                               indices.data(),\n                                               data_ + i * n_comp);\n\n      } else {\n        /* Non-vectorized sequential access. */\n\n        for (unsigned int d = 0; d < n_comp; ++d)\n          data_[i * n_comp + d] += tensor[d];\n      }\n    }\n\n\n    template <typename Number,\n              int n_comp,\n              int simd_l,\n              typename MemorySpace,\n              bool writable>\n    void\n    MultiComponentVectorView<Number, n_comp, simd_l, MemorySpace, writable>::\n        zero_out_ghost_values() const\n      requires(writable)\n    {\n      using HostSpace = dealii::MemorySpace::Host;\n      using DefaultSpace = dealii::MemorySpace::Default;\n      static_assert(std::is_same_v<MemorySpace, HostSpace> ||\n                        std::is_same_v<MemorySpace, DefaultSpace>,\n                    \"Unexpected memory space\");\n\n      Assert(multi_component_vector_\n                 ->template is_active_memory_space<MemorySpace>(),\n             dealii::ExcMessage(\"The chosen memory space is not active.\"));\n\n      multi_component_vector_\n          ->template zero_out_ghost_values_on_memory_space<MemorySpace>();\n    }\n\n\n    template <typename Number,\n              int n_comp,\n              int simd_l,\n              typename MemorySpace,\n              bool writable>\n    void\n    MultiComponentVectorView<Number, n_comp, simd_l, MemorySpace, writable>::\n        update_ghost_values() const\n      requires(writable)\n    {\n      using HostSpace = dealii::MemorySpace::Host;\n      using DefaultSpace = dealii::MemorySpace::Default;\n      static_assert(std::is_same_v<MemorySpace, HostSpace> ||\n                        std::is_same_v<MemorySpace, DefaultSpace>,\n                    \"Unexpected memory space\");\n\n      Assert(multi_component_vector_\n                 ->template is_active_memory_space<MemorySpace>(),\n             dealii::ExcMessage(\"The chosen memory space is not active.\"));\n\n      multi_component_vector_\n          ->template update_ghost_values_on_memory_space<MemorySpace>();\n    }\n\n\n    template <typename Number,\n              int n_comp,\n              int simd_l,\n              typename MemorySpace,\n              bool writable>\n    void\n    MultiComponentVectorView<Number, n_comp, simd_l, MemorySpace, writable>::\n        compress(dealii::VectorOperation::values operation) const\n      requires(writable)\n    {\n      using HostSpace = dealii::MemorySpace::Host;\n      using DefaultSpace = dealii::MemorySpace::Default;\n      static_assert(std::is_same_v<MemorySpace, HostSpace> ||\n                        std::is_same_v<MemorySpace, DefaultSpace>,\n                    \"Unexpected memory space\");\n\n      Assert(multi_component_vector_\n                 ->template is_active_memory_space<MemorySpace>(),\n             dealii::ExcMessage(\"The chosen memory space is not active.\"));\n\n      multi_component_vector_->template compress_on_memory_space<MemorySpace>(\n          operation);\n    }\n\n#endif\n  } // namespace Vectors\n} // namespace ryujin\n"
  },
  {
    "path": "source/navier_stokes/CMakeLists.txt",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2022 - 2025 by the ryujin authors\n##\n\nadd_library(obj_navier_stokes OBJECT\n  equation_dispatch.cc\n  initial_state_library.cc\n  parabolic_module.cc\n  )\nset_target_properties(obj_navier_stokes PROPERTIES LINKER_LANGUAGE CXX)\ndeal_ii_setup_target(obj_navier_stokes)\ntarget_link_libraries(obj_navier_stokes obj_common ${EXTERNAL_TARGETS})\n# Propagate the current source directory with PUBLIC visibility\ntarget_include_directories(obj_navier_stokes PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})\n"
  },
  {
    "path": "source/navier_stokes/Makefile",
    "content": "default: all\n.PHONY: default\n\n%:\n\t@cd .. && make $@\n.PHONY: %\n"
  },
  {
    "path": "source/navier_stokes/description.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"../euler/hyperbolic_system.h\"\n#include \"../euler/indicator.h\"\n#include \"../euler/limiter.h\"\n#include \"../euler/riemann_solver.h\"\n#include \"parabolic_module.h\"\n#include \"parabolic_system.h\"\n\nnamespace ryujin\n{\n  namespace NavierStokes\n  {\n    /**\n     * A struct that contains all equation specific classes describing the\n     * chosen hyperbolic system, the indicator, the limiter and\n     * (approximate) Riemann solver.\n     *\n     * We group all of these templates together in this struct so that we\n     * only need to add a single template parameter to the all the\n     * algorithm classes, such as HyperbolicModule.\n     *\n     * @ingroup NavierStokesEquations\n     */\n    struct Description {\n      using HyperbolicSystem = Euler::HyperbolicSystem;\n\n      template <int dim, typename Number = double>\n      using HyperbolicSystemView = Euler::HyperbolicSystemView<dim, Number>;\n\n      using ParabolicSystem = NavierStokes::ParabolicSystem;\n\n      template <int dim, typename Number = double>\n      using ParabolicModule = NavierStokes::ParabolicModule<dim, Number>;\n\n      template <int dim, typename Number = double>\n      using Indicator = Euler::Indicator<dim, Number>;\n\n      template <int dim, typename Number = double>\n      using Limiter = Euler::Limiter<dim, Number>;\n\n      template <int dim, typename Number = double>\n      using RiemannSolver = Euler::RiemannSolver<dim, Number>;\n    };\n  } // namespace NavierStokes\n} // namespace ryujin\n"
  },
  {
    "path": "source/navier_stokes/equation_dispatch.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2024 by the ryujin authors\n//\n\n#include \"description.h\"\n\n#include <compile_time_options.h>\n#include <equation_dispatch.h>\n\nnamespace ryujin\n{\n  namespace NavierStokes\n  {\n    Dispatch<Description, NUMBER> dispatch_instance(\"navier stokes\");\n  } // namespace NavierStokes\n} // namespace ryujin\n"
  },
  {
    "path": "source/navier_stokes/initial_state_library.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 by the ryujin authors\n//\n\n#include \"initial_state_library.template.h\"\n\nnamespace ryujin\n{\n  template class InitialStateLibrary<Description, 1, NUMBER>;\n  template class InitialStateLibrary<Description, 2, NUMBER>;\n  template class InitialStateLibrary<Description, 3, NUMBER>;\n} // namespace ryujin\n"
  },
  {
    "path": "source/navier_stokes/initial_state_library.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include \"description.h\"\n\n#include \"../euler/initial_state_library_euler.h\"\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  using Description = NavierStokes::Description;\n\n  template <int dim, typename Number>\n  class InitialStateLibrary<Description, dim, Number>\n  {\n  public:\n    using HyperbolicSystem = typename Description::HyperbolicSystem;\n    using ParabolicSystem = typename Description::ParabolicSystem;\n\n    using View =\n        typename Description::template HyperbolicSystemView<dim, Number>;\n\n    using initial_state_list_type =\n        std::set<std::unique_ptr<InitialState<Description, dim, Number>>>;\n\n    static void\n    populate_initial_state_list(initial_state_list_type &initial_state_list,\n                                const HyperbolicSystem &h,\n                                const ParabolicSystem & /*p*/,\n                                const std::string &s)\n    {\n      EulerInitialStates::populate_initial_state_list<Description, dim, Number>(\n          initial_state_list, h, s);\n    }\n  };\n} // namespace ryujin\n"
  },
  {
    "path": "source/navier_stokes/instantiate.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 by the ryujin authors\n//\n\n#ifndef RYUJIN_INCLUDE_INSTANTIATION_ONCE\n#define RYUJIN_INCLUDE_INSTANTIATION_ONCE\n#else\n#error Instantiation files can only be included once.\n#endif\n\n#include \"description.h\"\n\nnamespace ryujin\n{\n  using NavierStokes::Description;\n} // namespace ryujin\n"
  },
  {
    "path": "source/navier_stokes/parabolic_module.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2025 by the ryujin authors\n//\n\n#include \"description.h\"\n#include \"parabolic_module.template.h\"\n\n#define INSTANTIATE(dim, stages)                                               \\\n  template void ParabolicModule<dim, NUMBER>::backward_euler_step<stages>(     \\\n      const StateVector &,                                                     \\\n      const NUMBER,                                                            \\\n      std::array<std::reference_wrapper<const StateVector>, stages>,           \\\n      const std::array<NUMBER, stages>,                                        \\\n      StateVector &,                                                           \\\n      NUMBER) const\n\nnamespace ryujin\n{\n  namespace NavierStokes\n  {\n    template class ParabolicModule<1, NUMBER>;\n    template class ParabolicModule<2, NUMBER>;\n    template class ParabolicModule<3, NUMBER>;\n\n    INSTANTIATE(1, 0);\n    INSTANTIATE(1, 1);\n    INSTANTIATE(1, 2);\n    INSTANTIATE(1, 3);\n\n    INSTANTIATE(2, 0);\n    INSTANTIATE(2, 1);\n    INSTANTIATE(2, 2);\n    INSTANTIATE(2, 3);\n\n    INSTANTIATE(3, 0);\n    INSTANTIATE(3, 1);\n    INSTANTIATE(3, 2);\n    INSTANTIATE(3, 3);\n  } // namespace NavierStokes\n} // namespace ryujin\n"
  },
  {
    "path": "source/navier_stokes/parabolic_module.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"hyperbolic_module.h\"\n\n#include \"observer_pointer.h\"\n#include <convenience_macros.h>\n#include <initial_values.h>\n#include <mpi_ensemble.h>\n#include <offline_data.h>\n#include <simd.h>\n\n#include \"parabolic_module_gmg_operators.h\"\n\n#include <deal.II/base/mg_level_object.h>\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/timer.h>\n#include <deal.II/lac/la_parallel_block_vector.h>\n#include <deal.II/lac/precondition.h>\n#include <deal.II/lac/sparse_matrix.templates.h>\n#include <deal.II/lac/vector.h>\n#include <deal.II/matrix_free/matrix_free.h>\n#include <deal.II/multigrid/mg_base.h>\n#include <deal.II/multigrid/mg_smoother.h>\n#include <deal.II/multigrid/mg_transfer_matrix_free.h>\n\nnamespace ryujin\n{\n  namespace NavierStokes\n  {\n    template <int, typename>\n    class DiagonalMatrix;\n\n    struct Description;\n\n    /**\n     * Implicit backward-Euler time stepping for the parabolic limiting\n     * equation @cite ryujin-2021-2, Eq. 3.3:\n     * \\f{align}\n     *   \\newcommand{\\bbm}{{\\boldsymbol m}}\n     *   \\newcommand{\\bef}{{\\boldsymbol f}}\n     *   \\newcommand{\\bk}{{\\boldsymbol k}}\n     *   \\newcommand{\\bu}{{\\boldsymbol u}}\n     *   \\newcommand{\\bv}{{\\boldsymbol v}}\n     *   \\newcommand{\\bn}{{\\boldsymbol n}}\n     *   \\newcommand{\\pols}{{\\mathbb s}}\n     *   \\newcommand{\\Hflux}{\\bk}\n     *   &\\partial_t \\rho  =  0,\n     *   \\\\\n     *   &\\partial_t \\bbm - \\nabla\\cdot(\\pols(\\bv)) = \\bef,\n     *   \\\\\n     *   &\\partial_t E   + \\nabla\\cdot(\\Hflux(\\bu)- \\pols(\\bv) \\bv) =\n     * \\bef\\cdot\\bv,\n     *   \\\\\n     *   &\\bv_{|\\partial D}=\\boldsymbol 0, \\qquad \\Hflux(\\bu)\\cdot\\bn_{|\\partial\n     * D}=0 . \\f}\n     *\n     * Internally, the module first performs an implicit backward Euler\n     * step updating the velocity (see @cite ryujin-2021-2, Eq. 5.5):\n     * \\f{align}\n     *   \\begin{cases}\n     *     \\newcommand\\bsfV{{\\textbf V}}\n     *     \\newcommand{\\polB}{{\\mathbb B}}\n     *     \\newcommand{\\calI}{{\\mathcal I}}\n     *     \\newcommand\\bsfF{{\\textbf F}}\n     *     \\newcommand\\bsfM{{\\textbf M}}\n     *     \\newcommand{\\upint}{^\\circ}\n     *     \\newcommand{\\upbnd}{^\\partial}\n     *     \\newcommand{\\dt}{{\\tau}}\n     *     \\newcommand{\\calV}{{\\mathcal V}}\n     *     \\varrho^{n}_i m_i \\bsfV^{n+1} +\n     *     \\dt\\sum_{j\\in\\calI(i)} \\polB_{ij} \\bsfV^{n+1} =\n     *     m_i \\bsfM_i^{n} + \\dt m_i \\bsfF_i^{n+1},\n     *     & \\forall i\\in \\calV\\upint\n     *     \\\\[0.3em]\n     *     \\bsfV_i^{n+1} = \\boldsymbol 0, &  \\forall i\\in \\calV\\upbnd,\n     *   \\end{cases}\n     * \\f}\n     * We then postprocess and compute an internal energy update with an\n     * additional backward Euler step, (cf. @cite ryujin-2021-2, Eq. 5.13)\n     * \\f{align}\n     *     \\newcommand\\bsfV{{\\textbf V}}\n     *     \\newcommand\\sfe{{\\mathsf e}}\n     *     \\newcommand{\\upHnph}{^{\\text{H},n+1}}\n     *     \\newcommand{\\calI}{{\\mathcal I}}\n     *     \\newcommand\\sfK{{\\mathsf K}}\n     *     \\newcommand{\\calV}{{\\mathcal V}}\n     *     m_i \\varrho_i^{n}(\\sfe_i{\\upHnph} - \\sfe_i^{n})+\\dt\n     *     \\sum_{j\\in\\calI(i)} \\beta_{ij}\\sfe_i{\\upHnph}\n     *     = \\tfrac12 m_i\\|\\bsfV^{n+1}-\\bsfV^{n}\\|^2\n     *     + \\dt m_i\\sfK_i^{n+1}, \\qquad \\forall i\\in \\calV.\n     * \\f}\n     * The result is then transformed back into conserved quantities and\n     * written to the output vector.\n     *\n     * @note The backward Euler scheme is a fundamental building block for\n     * higher-order time stepping, including the well-known Crank-Nicolson\n     * scheme. The latter can be expressed algebraically as a backward\n     * Euler step (from time \\f$t\\f$ to \\f$t+\\tau\\f$ followed by an\n     * extrapolation step \\f$U^{n+2}=2U^{n+1}-U^{n}\\f$ from time\n     * \\f$t+\\tau\\f$ to \\f$t+2\\tau\\f$). This approach differs from the\n     * Crank-Nicolson scheme discussed in @cite ryujin-2021-2 where the\n     * extrapolation step is performed on the primitive quantities\n     * (velocity and internal energy) instead of the conserved quantities.\n     *\n     * @ingroup NavierStokesEquations\n     */\n    template <int dim, typename Number = double>\n    class ParabolicModule final : public dealii::ParameterAcceptor\n    {\n    public:\n      /**\n       * @name Typedefs and constexpr constants\n       */\n      //@{\n\n      using HyperbolicSystem = Euler::HyperbolicSystem;\n\n      using View = Euler::HyperbolicSystemView<dim, Number>;\n\n      using ParabolicSystem = NavierStokes::ParabolicSystem;\n\n      using StateVector = typename View::StateVector;\n\n      using ScalarHostVector = Vectors::ScalarHostVector<Number>;\n\n      using BlockHostVector = Vectors::BlockHostVector<Number>;\n\n      using ScalarNumber = typename View::ScalarNumber;\n\n      static constexpr auto problem_dimension = View::problem_dimension;\n\n      using state_type = typename View::state_type;\n\n      //@}\n      /**\n       * @name Constructor and setup\n       */\n      //@{\n\n      /**\n       * Constructor.\n       */\n      ParabolicModule(\n          const MPIEnsemble &mpi_ensemble,\n          std::map<std::string, dealii::Timer> &computing_timer,\n          const OfflineData<dim, Number> &offline_data,\n          const HyperbolicSystem &hyperbolic_system,\n          const ParabolicSystem &parabolic_system,\n          const InitialValues<Description, dim, Number> &initial_values,\n          const std::string &subsection = \"ParabolicModule\");\n\n      /**\n       * Prepare time stepping. A call to @ref prepare() allocates temporary\n       * storage and is necessary before any of the following time-stepping\n       * functions can be called.\n       */\n      void prepare();\n\n      //@}\n      /**\n       * @name Functons for performing explicit time steps\n       */\n      //@{\n\n      /**\n       * (Re)initialize the parabolic state vector component of the state\n       * vector.\n       *\n       * @note This routine does not modify the hyperbolic state vector or\n       * the precomputed vector component.\n       */\n      void reinit_state_vector(StateVector & /*state_vector*/) const\n      {\n        // do nothing\n      }\n\n      /**\n       * Prepare the parabolic part of the state vector prior to a backward\n       * Euler or Crank Nicolson step.\n       */\n      void prepare_state_vector(StateVector &state_vector, Number t) const;\n\n      /**\n       * Given a reference to a previous state vector @p old_U at time\n       * @p old_t and a time-step size @p tau perform an implicit backward\n       * euler step (and store the result in @p new_U).\n       *\n       * The function takes an optional array of states @p stage_U together\n       * with a an array of weights @p stage_weights to construct a modified\n       * high-order right-hand side / flux.\n       */\n      template <int stages>\n      void\n      backward_euler_step(const StateVector &old_state_vector,\n                          const Number old_t,\n                          std::array<std::reference_wrapper<const StateVector>,\n                                     stages> stage_state_vectors,\n                          const std::array<Number, stages> stage_weights,\n                          StateVector &new_state_vector,\n                          Number tau) const;\n\n      /**\n       * Given a reference to a previous state vector @p old_state_vector\n       * at time @p old_t and a time-step size @p tau perform an implicit\n       * Crank Nicolson step (and store the result in @p new_state_vector).\n       */\n      void crank_nicolson_step(const StateVector &old_state_vector,\n                               const Number old_t,\n                               StateVector &new_state_vector,\n                               Number tau) const;\n\n      /**\n       * Sets the invariant domain violation strategy.\n       */\n      void set_id_violation_strategy(const IDViolationStrategy &strategy) const\n      {\n        id_violation_strategy_ = strategy;\n      }\n\n      //@}\n      /**\n       * @name Information and statistics\n       */\n      //@{\n\n      /**\n       * Print a status line with solver statistics. This function is used\n       * for constructing the status message displayed periodically in the\n       * TimeLoop.\n       */\n      void print_solver_statistics(std::ostream &output) const;\n\n      /**\n       * The number of restarts signalled by the step() function.\n       */\n      ACCESSOR_READ_ONLY(n_restarts)\n\n      /**\n       * The number of corrections performed by the step() function. This\n       * function exists to mirror the ParabolicModule interface and will\n       * always return 0.\n       */\n      ACCESSOR_READ_ONLY(n_corrections)\n\n      /**\n       * The number of ID violation warnings encounterd in the step()\n       * function.\n       */\n      ACCESSOR_READ_ONLY(n_warnings)\n\n      //@}\n\n    private:\n      /**\n       * @name Run time options\n       */\n      //@{\n\n      bool use_gmg_velocity_;\n      bool use_gmg_internal_energy_;\n\n      Number tolerance_;\n      bool tolerance_linfty_norm_;\n\n      unsigned int gmg_max_iter_vel_;\n      unsigned int gmg_max_iter_en_;\n      double gmg_smoother_range_vel_;\n      double gmg_smoother_range_en_;\n      double gmg_smoother_max_eig_vel_;\n      double gmg_smoother_max_eig_en_;\n      unsigned int gmg_smoother_degree_;\n      unsigned int gmg_smoother_n_cg_iter_;\n      unsigned int gmg_min_level_;\n\n      //@}\n      /**\n       * @name Low-level implementation\n       */\n      //@{\n\n      /**\n       * Given a reference to a previous state vector @p old_state_vector\n       * at time @p old_t and a time-step size @p tau perform a backward\n       * Euler time step (and store the result in @p new_state_vector).\n       *\n       * If the boolean @crank_nicolson_extrapolation is set to true, then\n       * we perform a final extrapolation on the primitive state for time\n       * t + 2 * tau.\n       */\n      void step(const StateVector &old_state_vector,\n                const Number old_t,\n                StateVector &new_state_vector,\n                Number tau,\n                const bool crank_nicolson_extrapolation) const;\n\n      //@}\n      /**\n       * @name Internal data\n       */\n      //@{\n\n      // FIXME: refactor\n      static constexpr unsigned int order_fe = 1;\n      static constexpr unsigned int order_quad = 2;\n\n      const MPIEnsemble &mpi_ensemble_;\n      std::map<std::string, dealii::Timer> &computing_timer_;\n\n      dealii::ObserverPointer<const HyperbolicSystem> hyperbolic_system_;\n      dealii::ObserverPointer<const ParabolicSystem> parabolic_system_;\n      dealii::ObserverPointer<const ryujin::OfflineData<dim, Number>>\n          offline_data_;\n      dealii::ObserverPointer<\n          const ryujin::InitialValues<Description, dim, Number>>\n          initial_values_;\n\n      mutable IDViolationStrategy id_violation_strategy_;\n\n      mutable unsigned int cycle_;\n\n      mutable unsigned int n_restarts_;\n      mutable unsigned int n_corrections_;\n      mutable unsigned int n_warnings_;\n      mutable double n_iterations_velocity_;\n      mutable double n_iterations_internal_energy_;\n\n      mutable dealii::MatrixFree<dim, Number> matrix_free_;\n\n      mutable BlockHostVector velocity_;\n      mutable BlockHostVector velocity_rhs_;\n      mutable ScalarHostVector internal_energy_;\n      mutable ScalarHostVector internal_energy_rhs_;\n      mutable ScalarHostVector density_;\n\n      mutable dealii::MGLevelObject<dealii::MatrixFree<dim, float>>\n          level_matrix_free_;\n      mutable dealii::MGConstrainedDoFs mg_constrained_dofs_;\n      mutable dealii::MGLevelObject<\n          dealii::LinearAlgebra::distributed::Vector<float>>\n          level_density_;\n      mutable MGTransferVelocity<dim, float> mg_transfer_velocity_;\n      mutable dealii::MGLevelObject<VelocityMatrix<dim, float, Number>>\n          level_velocity_matrices_;\n      mutable MGTransferEnergy<dim, float> mg_transfer_energy_;\n      mutable dealii::MGLevelObject<EnergyMatrix<dim, float, Number>>\n          level_energy_matrices_;\n\n      mutable dealii::mg::SmootherRelaxation<\n          dealii::PreconditionChebyshev<\n              VelocityMatrix<dim, float, Number>,\n              dealii::LinearAlgebra::distributed::BlockVector<float>,\n              DiagonalMatrix<dim, float>>,\n          dealii::LinearAlgebra::distributed::BlockVector<float>>\n          mg_smoother_velocity_;\n\n      mutable dealii::mg::SmootherRelaxation<\n          dealii::PreconditionChebyshev<\n              EnergyMatrix<dim, float, Number>,\n              dealii::LinearAlgebra::distributed::Vector<float>>,\n          dealii::LinearAlgebra::distributed::Vector<float>>\n          mg_smoother_energy_;\n\n      //@}\n    };\n\n  } // namespace NavierStokes\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/navier_stokes/parabolic_module.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include \"parabolic_module.h\"\n\n#include <loop.h>\n#include <scope.h>\n#include <simd.h>\n\n#include <deal.II/lac/linear_operator.h>\n#include <deal.II/lac/precondition.h>\n#include <deal.II/lac/solver_cg.h>\n#include <deal.II/matrix_free/fe_evaluation.h>\n#include <deal.II/multigrid/mg_coarse.h>\n#include <deal.II/multigrid/mg_matrix.h>\n#include <deal.II/multigrid/mg_transfer.templates.h>\n#include <deal.II/multigrid/mg_transfer_matrix_free.h>\n#include <deal.II/multigrid/multigrid.h>\n\n#include <atomic>\n\nnamespace ryujin\n{\n  namespace NavierStokes\n  {\n    using namespace dealii;\n\n    template <int dim, typename Number>\n    ParabolicModule<dim, Number>::ParabolicModule(\n        const MPIEnsemble &mpi_ensemble,\n        std::map<std::string, dealii::Timer> &computing_timer,\n        const OfflineData<dim, Number> &offline_data,\n        const HyperbolicSystem &hyperbolic_system,\n        const ParabolicSystem &parabolic_system,\n        const InitialValues<Description, dim, Number> &initial_values,\n        const std::string &subsection /*= \"ParabolicModule\"*/)\n        : ParameterAcceptor(subsection)\n        , mpi_ensemble_(mpi_ensemble)\n        , computing_timer_(computing_timer)\n        , hyperbolic_system_(&hyperbolic_system)\n        , parabolic_system_(&parabolic_system)\n        , offline_data_(&offline_data)\n        , initial_values_(&initial_values)\n        , id_violation_strategy_(IDViolationStrategy::warn)\n        , cycle_(0)\n        , n_restarts_(0)\n        , n_corrections_(0)\n        , n_warnings_(0)\n        , n_iterations_velocity_(0.)\n        , n_iterations_internal_energy_(0.)\n    {\n      use_gmg_velocity_ = false;\n      add_parameter(\"multigrid velocity\",\n                    use_gmg_velocity_,\n                    \"Use geometric multigrid for velocity component\");\n\n      gmg_max_iter_vel_ = 12;\n      add_parameter(\"multigrid velocity - max iter\",\n                    gmg_max_iter_vel_,\n                    \"Maximal number of CG iterations with GMG smoother\");\n\n      gmg_smoother_range_vel_ = 8.;\n      add_parameter(\"multigrid velocity - chebyshev range\",\n                    gmg_smoother_range_vel_,\n                    \"Chebyshev smoother: eigenvalue range parameter\");\n\n      gmg_smoother_max_eig_vel_ = 2.0;\n      add_parameter(\"multigrid velocity - chebyshev max eig\",\n                    gmg_smoother_max_eig_vel_,\n                    \"Chebyshev smoother: maximal eigenvalue\");\n\n      use_gmg_internal_energy_ = false;\n      add_parameter(\"multigrid energy\",\n                    use_gmg_internal_energy_,\n                    \"Use geometric multigrid for internal energy component\");\n\n      gmg_max_iter_en_ = 15;\n      add_parameter(\"multigrid energy - max iter\",\n                    gmg_max_iter_en_,\n                    \"Maximal number of CG iterations with GMG smoother\");\n\n      gmg_smoother_range_en_ = 15.;\n      add_parameter(\"multigrid energy - chebyshev range\",\n                    gmg_smoother_range_en_,\n                    \"Chebyshev smoother: eigenvalue range parameter\");\n\n      gmg_smoother_max_eig_en_ = 2.0;\n      add_parameter(\"multigrid energy - chebyshev max eig\",\n                    gmg_smoother_max_eig_en_,\n                    \"Chebyshev smoother: maximal eigenvalue\");\n\n      gmg_smoother_degree_ = 3;\n      add_parameter(\"multigrid - chebyshev degree\",\n                    gmg_smoother_degree_,\n                    \"Chebyshev smoother: degree\");\n\n      gmg_smoother_n_cg_iter_ = 10;\n      add_parameter(\n          \"multigrid - chebyshev cg iter\",\n          gmg_smoother_n_cg_iter_,\n          \"Chebyshev smoother: number of CG iterations to approximate \"\n          \"eigenvalue\");\n\n      gmg_min_level_ = 0;\n      add_parameter(\n          \"multigrid - min level\",\n          gmg_min_level_,\n          \"Minimal mesh level to be visited in the geometric multigrid \"\n          \"cycle where the coarse grid solver (Chebyshev) is called\");\n\n      tolerance_ = Number(1.0e-12);\n      add_parameter(\"tolerance\", tolerance_, \"Tolerance for linear solvers\");\n\n      tolerance_linfty_norm_ = false;\n      add_parameter(\"tolerance linfty norm\",\n                    tolerance_linfty_norm_,\n                    \"Use the l_infty norm instead of the l_2 norm for the \"\n                    \"stopping criterion\");\n    }\n\n\n    template <int dim, typename Number>\n    void ParabolicModule<dim, Number>::prepare()\n    {\n#ifdef DEBUG_OUTPUT\n      std::cout << \"ParabolicModule<dim, Number>::prepare()\" << std::endl;\n#endif\n      /*\n       * The cycle_ variabe is only used for gmg reinitialization, simply\n       * reset it to zero on prepare().\n       */\n      cycle_ = 0;\n\n      const auto &discretization = offline_data_->discretization();\n      AssertThrow(discretization.ansatz() == Ansatz::cg_q1,\n                  dealii::ExcMessage(\"The Navier-Stokes module currently only \"\n                                     \"supports cG Q1 finite elements.\"));\n\n      AssertThrow(!offline_data_->dof_handler().has_hp_capabilities(),\n                  dealii::ExcMessage(\n                      \"The Navier-Stokes module currently does not support \"\n                      \"DoFHandlers set up with hp capabilities.\"));\n\n      /* Initialize vectors: */\n\n      typename MatrixFree<dim, Number>::AdditionalData additional_data;\n      additional_data.tasks_parallel_scheme =\n          MatrixFree<dim, Number>::AdditionalData::none;\n\n      matrix_free_.reinit(discretization.mapping(),\n                          offline_data_->dof_handler(),\n                          offline_data_->affine_constraints(),\n                          discretization.quadrature_1d(),\n                          additional_data);\n\n      const auto &scalar_partitioner =\n          matrix_free_.get_dof_info(0).vector_partitioner;\n\n      velocity_.reinit(dim);\n      velocity_rhs_.reinit(dim);\n      for (unsigned int i = 0; i < dim; ++i) {\n        velocity_.block(i).reinit(scalar_partitioner);\n        velocity_rhs_.block(i).reinit(scalar_partitioner);\n      }\n\n      internal_energy_.reinit(scalar_partitioner);\n      internal_energy_rhs_.reinit(scalar_partitioner);\n\n      density_.reinit(scalar_partitioner);\n\n      /* Initialize multigrid: */\n\n      if (!use_gmg_velocity_ && !use_gmg_internal_energy_)\n        return;\n\n      const unsigned int n_levels =\n          offline_data_->dof_handler().get_triangulation().n_global_levels();\n      const unsigned int min_level = std::min(gmg_min_level_, n_levels - 1);\n      MGLevelObject<IndexSet> relevant_sets(0, n_levels - 1);\n      for (unsigned int level = 0; level < n_levels; ++level)\n#if DEAL_II_VERSION_GTE(9, 6, 0)\n        relevant_sets[level] =\n            dealii::DoFTools::extract_locally_relevant_level_dofs(\n                offline_data_->dof_handler(), level);\n#else\n        dealii::DoFTools::extract_locally_relevant_level_dofs(\n            offline_data_->dof_handler(), level, relevant_sets[level]);\n#endif\n      mg_constrained_dofs_.initialize(offline_data_->dof_handler(),\n                                      relevant_sets);\n      std::set<types::boundary_id> boundary_ids;\n      boundary_ids.insert(Boundary::dirichlet);\n      boundary_ids.insert(Boundary::no_slip);\n      mg_constrained_dofs_.make_zero_boundary_constraints(\n          offline_data_->dof_handler(), boundary_ids);\n\n      typename MatrixFree<dim, float>::AdditionalData additional_data_level;\n      additional_data_level.tasks_parallel_scheme =\n          MatrixFree<dim, float>::AdditionalData::none;\n\n      level_matrix_free_.resize(min_level, n_levels - 1);\n      level_density_.resize(min_level, n_levels - 1);\n      for (unsigned int level = min_level; level < n_levels; ++level) {\n        additional_data_level.mg_level = level;\n#if DEAL_II_VERSION_GTE(9, 6, 0)\n        AffineConstraints<double> constraints(relevant_sets[level],\n                                              relevant_sets[level]);\n#else\n        AffineConstraints<double> constraints(relevant_sets[level]);\n#endif\n        // constraints.add_lines(mg_constrained_dofs_.get_boundary_indices(level));\n        // constraints.merge(mg_constrained_dofs_.get_level_constraints(level));\n        constraints.close();\n        level_matrix_free_[level].reinit(discretization.mapping(),\n                                         offline_data_->dof_handler(),\n                                         constraints,\n                                         discretization.quadrature_1d(),\n                                         additional_data_level);\n        level_matrix_free_[level].initialize_dof_vector(level_density_[level]);\n      }\n\n      mg_transfer_velocity_.build(offline_data_->dof_handler(),\n                                  mg_constrained_dofs_,\n                                  level_matrix_free_);\n      mg_transfer_energy_.build(offline_data_->dof_handler(),\n                                level_matrix_free_);\n    }\n\n\n    template <int dim, typename Number>\n    void ParabolicModule<dim, Number>::prepare_state_vector(\n        StateVector & /*state_vector*/, Number /*t*/) const\n    {\n      /*\n       * There is no parabolic part of the state vector for Navier-Stokes,\n       * so we do nothing.\n       */\n    }\n\n\n    template <int dim, typename Number>\n    template <int stages>\n    void ParabolicModule<dim, Number>::backward_euler_step(\n        const StateVector &old_state_vector,\n        const Number old_t,\n        std::array<std::reference_wrapper<const StateVector>,\n                   stages> /*stage_state_vectors*/,\n        const std::array<Number, stages> /*stage_weights*/,\n        StateVector &new_state_vector,\n        Number tau) const\n    {\n      /* Backward Euler step to half time step, and extrapolate: */\n\n      step(old_state_vector,\n           old_t,\n           new_state_vector,\n           tau,\n           /*crank_nicolson_extrapolation = */ false);\n    }\n\n\n    template <int dim, typename Number>\n    void ParabolicModule<dim, Number>::crank_nicolson_step(\n        const StateVector &old_state_vector,\n        const Number old_t,\n        StateVector &new_state_vector,\n        Number tau) const\n    {\n      try {\n        step(old_state_vector,\n             old_t,\n             new_state_vector,\n             tau / Number(2.),\n             /*crank_nicolson_extrapolation = */ true);\n\n      } catch (Correction) {\n\n        /*\n         * Under very rare circumstances we might fail to perform a Crank\n         * Nicolson step because the extrapolation step produced\n         * inadmissible states. We could correct the update now by\n         * performing a limiting step (either convex limiting, or flux\n         * corrected transport)... but *meh*, just perform a backward Euler\n         * step:\n         */\n        step(old_state_vector,\n             old_t,\n             new_state_vector,\n             tau,\n             /*crank_nicolson_extrapolation = */ false);\n      }\n    }\n\n\n    template <int dim, typename Number>\n    void ParabolicModule<dim, Number>::step(\n        const StateVector &old_state_vector,\n        const Number t,\n        StateVector &new_state_vector,\n        Number tau,\n        const bool crank_nicolson_extrapolation) const\n    {\n#ifdef DEBUG_OUTPUT\n      std::cout << \"ParabolicModule<dim, Number>::step()\" << std::endl;\n#endif\n      constexpr ScalarNumber eps = std::numeric_limits<ScalarNumber>::epsilon();\n\n      const auto &old_U = std::get<0>(old_state_vector);\n      auto &new_U = std::get<0>(new_state_vector);\n\n      const auto &lumped_mass_matrix = offline_data_->lumped_mass_matrix();\n      const auto &affine_constraints = offline_data_->affine_constraints();\n\n      /* Index ranges for the iteration over the sparsity pattern : */\n\n      const unsigned int n_owned = offline_data_->n_locally_owned();\n\n      const auto &sparsity_simd = offline_data_->sparsity_pattern_simd();\n\n      DiagonalMatrix<dim, Number> diagonal_matrix;\n\n#ifdef DEBUG_OUTPUT\n      std::cout << \"        perform time-step with tau = \" << tau << std::endl;\n      if (crank_nicolson_extrapolation)\n        std::cout << \"        and extrapolate to t + 2 * tau\" << std::endl;\n#endif\n\n      /*\n       * Update MG matrices all 4 time steps; this is a balance because more\n       * refreshes will render the approximation better, at some additional\n       * cost.\n       */\n      const bool reinitialize_gmg = (cycle_++ % 4 == 0);\n\n      /*\n       * A boolean indicating that a restart is required.\n       *\n       * In our current implementation we set this boolean to true if the\n       * backward Euler step produces an internal energy update that\n       * violates the minimum principle, i.e., the minimum of the new\n       * internal energy is smaller than the minimum of the old internal\n       * energy.\n       *\n       * Depending on the chosen \"id_violation_strategy\" we either signal a\n       * restart by throwing a \"Restart\" object, or we simply increase the\n       * number of warnings.\n       */\n      std::atomic<bool> restart_needed = false;\n\n      /*\n       * A boolean indicating that we have to correct the high-order Crank\n       * Nicolson update. Note that this is a truly exceptional case\n       * indicating that the high-order update produced an inadmissible\n       * state, *boo*.\n       *\n       * Our current limiting strategy is to simply fall back to perform a\n       * single backward Euler step...\n       */\n      std::atomic<bool> correction_needed = false;\n\n      /*\n       * Step 1:\n       *\n       * Build right hand side for the velocity update.\n       * Also initialize solution vectors for internal energy and velocity\n       * update.\n       */\n      {\n        Scope scope(computing_timer_, \"time step [P] 1 - update velocities\");\n\n        const auto body = [&](auto sentinel, unsigned int i) {\n          using T = decltype(sentinel);\n\n          const auto view = hyperbolic_system_->template view<dim, T>();\n\n          const auto U_i = old_U.template read_tensor<T>(i);\n          const auto rho_i = view.density(U_i);\n          const auto M_i = view.momentum(U_i);\n          const auto rho_e_i = view.internal_energy(U_i);\n          const auto m_i = lumped_mass_matrix.template read_entry<T>(i);\n\n          write_entry<T>(density_, rho_i, i);\n          /* (5.4a) */\n          for (unsigned int d = 0; d < dim; ++d) {\n            write_entry<T>(velocity_.block(d), M_i[d] / rho_i, i);\n            write_entry<T>(velocity_rhs_.block(d), m_i * (M_i[d]), i);\n          }\n          write_entry<T>(internal_energy_, rho_e_i / rho_i, i);\n        };\n\n        cpu_simd_loop<Number>(\n            \"time_step_parabolic_1\", body, 0, n_owned, n_owned);\n\n        /*\n         * Set up \"strongly enforced\" boundary conditions that are not stored\n         * in the AffineConstraints map. In this case we enforce boundary\n         * values by imposing them strongly in the iteration by setting the\n         * initial vector and the right hand side to the right value:\n         */\n\n        const auto &boundary_map = offline_data_->boundary_map();\n\n        for (auto entry : boundary_map) {\n          // [i, normal, normal_mass, boundary_mass, id, position] = entry\n          const auto i = std::get<0>(entry);\n          if (i >= n_owned)\n            continue;\n\n          const auto normal = std::get<1>(entry);\n          const auto id = std::get<4>(entry);\n          const auto position = std::get<5>(entry);\n\n          if (id == Boundary::slip) {\n            /* Remove normal component of velocity: */\n            Tensor<1, dim, Number> V_i;\n            Tensor<1, dim, Number> RHS_i;\n            for (unsigned int d = 0; d < dim; ++d) {\n              V_i[d] = velocity_.block(d).local_element(i);\n              RHS_i[d] = velocity_rhs_.block(d).local_element(i);\n            }\n            V_i -= 1. * (V_i * normal) * normal;\n            RHS_i -= 1. * (RHS_i * normal) * normal;\n            for (unsigned int d = 0; d < dim; ++d) {\n              velocity_.block(d).local_element(i) = V_i[d];\n              velocity_rhs_.block(d).local_element(i) = RHS_i[d];\n            }\n\n          } else if (id == Boundary::no_slip) {\n\n            /* Set velocity to zero: */\n            for (unsigned int d = 0; d < dim; ++d) {\n              velocity_.block(d).local_element(i) = Number(0.);\n              velocity_rhs_.block(d).local_element(i) = Number(0.);\n            }\n\n          } else if (id == Boundary::dirichlet) {\n\n            /* Prescribe velocity: */\n            const auto U_i = initial_values_->initial_state(position, t + tau);\n            const auto view = hyperbolic_system_->template view<dim, Number>();\n            const auto rho_i = view.density(U_i);\n            const auto V_i = view.momentum(U_i) / rho_i;\n            const auto e_i = view.internal_energy(U_i) / rho_i;\n\n            for (unsigned int d = 0; d < dim; ++d) {\n              velocity_.block(d).local_element(i) = V_i[d];\n              velocity_rhs_.block(d).local_element(i) = V_i[d];\n            }\n\n            internal_energy_.local_element(i) = e_i;\n          }\n        }\n\n        /*\n         * Zero out constrained degrees of freedom due to hanging nodes and\n         * periodic boundary conditions. These boundary conditions are\n         * enforced by modifying the stencil - consequently we have to\n         * remove constrained dofs from the linear system.\n         */\n\n        affine_constraints.set_zero(density_);\n        affine_constraints.set_zero(internal_energy_);\n        for (unsigned int d = 0; d < dim; ++d) {\n          affine_constraints.set_zero(velocity_.block(d));\n          affine_constraints.set_zero(velocity_rhs_.block(d));\n        }\n\n        /* Prepare preconditioner: */\n\n        diagonal_matrix.reinit(\n            lumped_mass_matrix, density_, affine_constraints);\n\n        if (use_gmg_velocity_ && reinitialize_gmg) {\n          MGLevelObject<typename PreconditionChebyshev<\n              VelocityMatrix<dim, float, Number>,\n              LinearAlgebra::distributed::BlockVector<float>,\n              DiagonalMatrix<dim, float>>::AdditionalData>\n              smoother_data(level_matrix_free_.min_level(),\n                            level_matrix_free_.max_level());\n\n          level_velocity_matrices_.resize(level_matrix_free_.min_level(),\n                                          level_matrix_free_.max_level());\n          mg_transfer_velocity_.interpolate_to_mg(\n              offline_data_->dof_handler(), level_density_, density_);\n\n          for (unsigned int level = level_matrix_free_.min_level();\n               level <= level_matrix_free_.max_level();\n               ++level) {\n            level_velocity_matrices_[level].initialize(\n                *parabolic_system_,\n                *offline_data_,\n                level_matrix_free_[level],\n                level_density_[level],\n                tau,\n                level);\n            level_velocity_matrices_[level].compute_diagonal(\n                smoother_data[level].preconditioner);\n            if (level == level_matrix_free_.min_level()) {\n              smoother_data[level].degree = numbers::invalid_unsigned_int;\n              smoother_data[level].eig_cg_n_iterations = 500;\n              smoother_data[level].smoothing_range = 1e-3;\n            } else {\n              smoother_data[level].degree = gmg_smoother_degree_;\n              smoother_data[level].eig_cg_n_iterations =\n                  gmg_smoother_n_cg_iter_;\n              smoother_data[level].smoothing_range = gmg_smoother_range_vel_;\n              if (gmg_smoother_n_cg_iter_ == 0)\n                smoother_data[level].max_eigenvalue = gmg_smoother_max_eig_vel_;\n            }\n          }\n          mg_smoother_velocity_.initialize(level_velocity_matrices_,\n                                           smoother_data);\n        }\n      }\n\n      Number e_min_old;\n\n      {\n        Scope scope(computing_timer_,\n                    \"time step [X] _ - synchronization barriers\");\n\n        /* Compute the global minimum of the internal energy: */\n\n        // .begin() and .end() denote the locally owned index range:\n        e_min_old =\n            *std::min_element(internal_energy_.begin(), internal_energy_.end());\n\n        e_min_old = Utilities::MPI::min(e_min_old,\n                                        mpi_ensemble_.ensemble_communicator());\n\n        // FIXME: create a meaningful relaxation based on global mesh size min.\n        constexpr Number eps = std::numeric_limits<Number>::epsilon();\n        e_min_old *= (1. - 1000. * eps);\n      }\n\n      /*\n       * Step 1: Solve velocity update:\n       */\n      {\n        Scope scope(computing_timer_, \"time step [P] 1 - update velocities\");\n\n        VelocityMatrix<dim, Number, Number> velocity_operator;\n        velocity_operator.initialize(\n            *parabolic_system_, *offline_data_, matrix_free_, density_, tau);\n\n        const auto tolerance_velocity =\n            (tolerance_linfty_norm_ ? velocity_rhs_.linfty_norm()\n                                    : velocity_rhs_.l2_norm()) *\n            tolerance_;\n\n        /*\n         * Multigrid might lack robustness for some cases, so in case it takes\n         * too many iterations we better switch to the more robust plain\n         * conjugate gradient method.\n         */\n        try {\n          if (!use_gmg_velocity_)\n            throw SolverControl::NoConvergence(0, 0.);\n\n          using bvt_float = LinearAlgebra::distributed::BlockVector<float>;\n\n          MGCoarseGridApplySmoother<bvt_float> mg_coarse;\n          mg_coarse.initialize(mg_smoother_velocity_);\n\n          mg::Matrix<bvt_float> mg_matrix(level_velocity_matrices_);\n\n          Multigrid<bvt_float> mg(mg_matrix,\n                                  mg_coarse,\n                                  mg_transfer_velocity_,\n                                  mg_smoother_velocity_,\n                                  mg_smoother_velocity_,\n                                  level_velocity_matrices_.min_level(),\n                                  level_velocity_matrices_.max_level());\n\n          const auto &dof_handler = offline_data_->dof_handler();\n          PreconditionMG<dim, bvt_float, MGTransferVelocity<dim, float>>\n              preconditioner(dof_handler, mg, mg_transfer_velocity_);\n\n          SolverControl solver_control(gmg_max_iter_vel_, tolerance_velocity);\n          SolverCG<BlockHostVector> solver(solver_control);\n          solver.solve(\n              velocity_operator, velocity_, velocity_rhs_, preconditioner);\n\n          /* update exponential moving average */\n          n_iterations_velocity_ =\n              0.9 * n_iterations_velocity_ + 0.1 * solver_control.last_step();\n\n        } catch (SolverControl::NoConvergence &) {\n\n          SolverControl solver_control(1000, tolerance_velocity);\n          SolverCG<BlockHostVector> solver(solver_control);\n          solver.solve(\n              velocity_operator, velocity_, velocity_rhs_, diagonal_matrix);\n\n          /* update exponential moving average, counting also GMG iterations */\n          n_iterations_velocity_ *= 0.9;\n          n_iterations_velocity_ +=\n              0.1 * (use_gmg_velocity_ ? gmg_max_iter_vel_ : 0) +\n              0.1 * solver_control.last_step();\n        }\n      }\n\n      /*\n       * Step 2: Build internal energy right hand side:\n       */\n      {\n        Scope scope(computing_timer_,\n                    \"time step [P] 2 - update internal energy\");\n\n        /* Compute m_i K_i^{n+1/2}:  (5.5) */\n        matrix_free_.template cell_loop<ScalarHostVector, BlockHostVector>(\n            [this](const auto &data,\n                   auto &dst,\n                   const auto &src,\n                   const auto cell_range) {\n              FEEvaluation<dim, order_fe, order_quad, dim, Number> velocity(\n                  data);\n              FEEvaluation<dim, order_fe, order_quad, 1, Number> energy(data);\n\n              const auto mu = parabolic_system_->mu();\n              const auto lambda = parabolic_system_->lambda();\n\n              for (unsigned int cell = cell_range.first;\n                   cell < cell_range.second;\n                   ++cell) {\n                velocity.reinit(cell);\n                energy.reinit(cell);\n                velocity.gather_evaluate(src, EvaluationFlags::gradients);\n\n                for (unsigned int q = 0; q < velocity.n_q_points; ++q) {\n                  if constexpr (dim == 1) {\n                    /* Workaround: no symmetric gradient for dim == 1: */\n                    const auto gradient = velocity.get_gradient(q);\n                    auto S = (4. / 3. * mu + lambda) * gradient;\n                    energy.submit_value(gradient * S, q);\n\n                  } else {\n\n                    const auto symmetric_gradient =\n                        velocity.get_symmetric_gradient(q);\n                    const auto divergence = trace(symmetric_gradient);\n                    auto S = 2. * mu * symmetric_gradient;\n                    for (unsigned int d = 0; d < dim; ++d)\n                      S[d][d] += (lambda - 2. / 3. * mu) * divergence;\n                    energy.submit_value(symmetric_gradient * S, q);\n                  }\n                }\n                energy.integrate_scatter(EvaluationFlags::values, dst);\n              }\n            },\n            internal_energy_rhs_,\n            velocity_,\n            /* zero destination */ true);\n\n        const auto &lumped_mass_matrix = offline_data_->lumped_mass_matrix();\n\n        const auto body = [&](auto sentinel, unsigned int i) {\n          using T = decltype(sentinel);\n\n          const auto view = hyperbolic_system_->template view<dim, T>();\n\n          const auto rhs_i = read_entry<T>(internal_energy_rhs_, i);\n          const auto m_i = lumped_mass_matrix.template read_entry<T>(i);\n          const auto rho_i = read_entry<T>(density_, i);\n          const auto e_i = read_entry<T>(internal_energy_, i);\n\n          const auto U_i = old_U.template read_tensor<T>(i);\n          const auto V_i = view.momentum(U_i) / rho_i;\n\n          dealii::Tensor<1, dim, T> V_i_new;\n          for (unsigned int d = 0; d < dim; ++d) {\n            V_i_new[d] = read_entry<T>(velocity_.block(d), i);\n          }\n\n          /*\n           * For backward Euler we have to add this algebraic correction\n           * to ensure conservation of total energy.\n           */\n          const auto correction =\n              crank_nicolson_extrapolation\n                  ? T(0.)\n                  : Number(0.5) * (V_i - V_i_new).norm_square();\n\n          /* rhs_i contains already m_i K_i^{n+1/2} */\n          const auto result = m_i * rho_i * (e_i + correction) + tau * rhs_i;\n          write_entry<T>(internal_energy_rhs_, result, i);\n        };\n\n        cpu_simd_loop<Number>(\n            \"time_step_parabolic_2\", body, 0, n_owned, n_owned);\n\n        /*\n         * Set up \"strongly enforced\" boundary conditions that are not stored\n         * in the AffineConstraints map: We enforce Neumann conditions (i.e.,\n         * insulating boundary conditions) everywhere except for Dirichlet\n         * boundaries where we have to enforce prescribed conditions:\n         */\n\n        const auto &boundary_map = offline_data_->boundary_map();\n\n        for (auto entry : boundary_map) {\n          // [i, normal, normal_mass, boundary_mass, id, position] = entry\n          const auto i = std::get<0>(entry);\n          if (i >= n_owned)\n            continue;\n\n          const auto id = std::get<4>(entry);\n          const auto position = std::get<5>(entry);\n\n          if (id == Boundary::dirichlet) {\n            /* Prescribe internal energy: */\n            const auto U_i = initial_values_->initial_state(position, t + tau);\n            const auto view = hyperbolic_system_->template view<dim, Number>();\n            const auto rho_i = view.density(U_i);\n            const auto e_i = view.internal_energy(U_i) / rho_i;\n            internal_energy_rhs_.local_element(i) = e_i;\n          }\n        }\n\n        /*\n         * Zero out constrained degrees of freedom due to hanging nodes and\n         * periodic boundary conditions. These boundary conditions are\n         * enforced by modifying the stencil - consequently we have to\n         * remove constrained dofs from the linear system.\n         */\n        affine_constraints.set_zero(internal_energy_);\n        affine_constraints.set_zero(internal_energy_rhs_);\n\n        /*\n         * Update MG matrices all 4 time steps; this is a balance because more\n         * refreshes will render the approximation better, at some additional\n         * cost.\n         */\n        if (use_gmg_internal_energy_ && reinitialize_gmg) {\n          MGLevelObject<typename PreconditionChebyshev<\n              EnergyMatrix<dim, float, Number>,\n              LinearAlgebra::distributed::Vector<float>>::AdditionalData>\n              smoother_data(level_matrix_free_.min_level(),\n                            level_matrix_free_.max_level());\n\n          level_energy_matrices_.resize(level_matrix_free_.min_level(),\n                                        level_matrix_free_.max_level());\n\n          for (unsigned int level = level_matrix_free_.min_level();\n               level <= level_matrix_free_.max_level();\n               ++level) {\n            level_energy_matrices_[level].initialize(\n                *offline_data_,\n                level_matrix_free_[level],\n                level_density_[level],\n                tau * parabolic_system_->cv_inverse_kappa(),\n                level);\n            level_energy_matrices_[level].compute_diagonal(\n                smoother_data[level].preconditioner);\n            if (level == level_matrix_free_.min_level()) {\n              smoother_data[level].degree = numbers::invalid_unsigned_int;\n              smoother_data[level].eig_cg_n_iterations = 500;\n              smoother_data[level].smoothing_range = 1e-3;\n            } else {\n              smoother_data[level].degree = gmg_smoother_degree_;\n              smoother_data[level].eig_cg_n_iterations =\n                  gmg_smoother_n_cg_iter_;\n              smoother_data[level].smoothing_range = gmg_smoother_range_en_;\n              if (gmg_smoother_n_cg_iter_ == 0)\n                smoother_data[level].max_eigenvalue = gmg_smoother_max_eig_en_;\n            }\n          }\n          mg_smoother_energy_.initialize(level_energy_matrices_, smoother_data);\n        }\n      }\n\n      /*\n       * Step 2: Solve internal energy update:\n       */\n      {\n        Scope scope(computing_timer_,\n                    \"time step [P] 2 - update internal energy\");\n\n        EnergyMatrix<dim, Number, Number> energy_operator;\n        const auto &kappa = parabolic_system_->cv_inverse_kappa();\n        energy_operator.initialize(\n            *offline_data_, matrix_free_, density_, tau * kappa);\n\n        const auto tolerance_internal_energy =\n            (tolerance_linfty_norm_ ? internal_energy_rhs_.linfty_norm()\n                                    : internal_energy_rhs_.l2_norm()) *\n            tolerance_;\n\n        try {\n          if (!use_gmg_internal_energy_)\n            throw SolverControl::NoConvergence(0, 0.);\n\n          using vt_float = LinearAlgebra::distributed::Vector<float>;\n          MGCoarseGridApplySmoother<vt_float> mg_coarse;\n          mg_coarse.initialize(mg_smoother_energy_);\n          mg::Matrix<vt_float> mg_matrix(level_energy_matrices_);\n\n          Multigrid<vt_float> mg(mg_matrix,\n                                 mg_coarse,\n                                 mg_transfer_energy_,\n                                 mg_smoother_energy_,\n                                 mg_smoother_energy_,\n                                 level_energy_matrices_.min_level(),\n                                 level_energy_matrices_.max_level());\n\n          const auto &dof_handler = offline_data_->dof_handler();\n          PreconditionMG<dim, vt_float, MGTransferEnergy<dim, float>>\n              preconditioner(dof_handler, mg, mg_transfer_energy_);\n\n          SolverControl solver_control(gmg_max_iter_en_,\n                                       tolerance_internal_energy);\n          SolverCG<ScalarHostVector> solver(solver_control);\n          solver.solve(energy_operator,\n                       internal_energy_,\n                       internal_energy_rhs_,\n                       preconditioner);\n\n          /* update exponential moving average */\n          n_iterations_internal_energy_ = 0.9 * n_iterations_internal_energy_ +\n                                          0.1 * solver_control.last_step();\n\n        } catch (SolverControl::NoConvergence &) {\n\n          SolverControl solver_control(1000, tolerance_internal_energy);\n          SolverCG<ScalarHostVector> solver(solver_control);\n          solver.solve(energy_operator,\n                       internal_energy_,\n                       internal_energy_rhs_,\n                       diagonal_matrix);\n\n          /* update exponential moving average, counting also GMG iterations */\n          n_iterations_internal_energy_ *= 0.9;\n          n_iterations_internal_energy_ +=\n              0.1 * (use_gmg_internal_energy_ ? gmg_max_iter_en_ : 0) +\n              0.1 * solver_control.last_step();\n        }\n      }\n\n      /*\n       * Step 3: Copy vectors and check for local minimum principle on\n       * internal energy:\n       *\n       * FIXME: Memory access is suboptimal...\n       */\n      {\n        Scope scope(computing_timer_, \"time step [P] 3 - write back vectors\");\n\n        const auto body = [&](auto sentinel, unsigned int i) {\n          using T = decltype(sentinel);\n\n          const auto view = hyperbolic_system_->template view<dim, T>();\n\n          /* Skip constrained degrees of freedom: */\n          const unsigned int row_length = sparsity_simd.row_length(i);\n          if (row_length == 1)\n            return;\n\n          auto U_i = old_U.template read_tensor<T>(i);\n          const auto rho_i = view.density(U_i);\n\n          Tensor<1, dim, T> m_i_new;\n          for (unsigned int d = 0; d < dim; ++d) {\n            m_i_new[d] = rho_i * read_entry<T>(velocity_.block(d), i);\n          }\n\n          auto rho_e_i_new = rho_i * read_entry<T>(internal_energy_, i);\n\n          /*\n           * Check that the backward Euler step itself (which is our \"low\n           * order\" update) satisfies bounds. If not, signal a restart.\n           */\n\n          if (!(T(0.) == std::max(T(0.), rho_i * e_min_old - rho_e_i_new))) {\n#ifdef DEBUG_OUTPUT\n            std::cout << std::fixed << std::setprecision(16);\n            const auto e_i_new = rho_e_i_new / rho_i;\n            std::cout << \"Bounds violation: internal energy (critical)!\\n\"\n                      << \"\\t\\te_min_old:         \" << e_min_old << \"\\n\"\n                      << \"\\t\\te_min_old (delta): \"\n                      << negative_part(e_i_new - e_min_old) << \"\\n\"\n                      << \"\\t\\te_min_new:         \" << e_i_new << \"\\n\"\n                      << std::endl;\n#endif\n            restart_needed = true;\n          }\n\n          if (crank_nicolson_extrapolation) {\n            m_i_new = Number(2.0) * m_i_new - view.momentum(U_i);\n            rho_e_i_new = Number(2.0) * rho_e_i_new - view.internal_energy(U_i);\n\n            /*\n             * If we do perform an extrapolation step for Crank Nicolson\n             * we have to check whether we maintain admissibility\n             */\n\n            if (!(T(0.) ==\n                  std::max(T(0.), eps * rho_i * e_min_old - rho_e_i_new))) {\n#ifdef DEBUG_OUTPUT\n              std::cout << std::fixed << std::setprecision(16);\n              const auto e_i_new = rho_e_i_new / rho_i;\n\n              std::cout << \"Bounds violation: high-order internal energy!\"\n                        << \"\\t\\te_min_new:         \" << e_i_new << \"\\n\"\n                        << \"\\t\\t-- correction required --\" << std::endl;\n#endif\n              correction_needed = true;\n            }\n          }\n\n          const auto E_i_new = rho_e_i_new + 0.5 * m_i_new * m_i_new / rho_i;\n\n          for (unsigned int d = 0; d < dim; ++d)\n            U_i[1 + d] = m_i_new[d];\n          U_i[1 + dim] = E_i_new;\n\n          new_U.template write_tensor<T>(U_i, i);\n        };\n\n        cpu_simd_loop<Number>(\n            \"time_step_parabolic_3\", body, 0, n_owned, n_owned);\n\n        new_U.update_ghost_values();\n      }\n\n      {\n        Scope scope(computing_timer_,\n                    \"time step [X] _ - synchronization barriers\");\n\n        /*\n         * Synchronize whether we have to restart or correct the time step.\n         * Even though the restart/correction condition itself only affects\n         * the local ensemble we nevertheless need to synchronize the\n         * boolean in case we perform synchronized global time steps.\n         * (Otherwise different ensembles might end up with a different\n         * time step.)\n         */\n\n        restart_needed.store(Utilities::MPI::logical_or(\n            restart_needed.load(),\n            mpi_ensemble_.synchronization_communicator()));\n\n        correction_needed.store(Utilities::MPI::logical_or(\n            correction_needed.load(),\n            mpi_ensemble_.synchronization_communicator()));\n      }\n\n      if (correction_needed) {\n        /* If we can do a restart try that first: */\n        if (id_violation_strategy_ == IDViolationStrategy::raise_exception) {\n          n_restarts_++;\n          /* Half step size is a good heuristic: */\n          throw Restart{Number(0.5) * tau};\n        } else {\n          n_corrections_++;\n          throw Correction();\n        }\n      }\n\n      if (restart_needed) {\n        switch (id_violation_strategy_) {\n        case IDViolationStrategy::warn:\n          n_warnings_++;\n          break;\n        case IDViolationStrategy::raise_exception:\n          n_restarts_++;\n          /* Half step size is a good heuristic: */\n          throw Restart{Number(0.5) * tau};\n        }\n      }\n    }\n\n\n    template <int dim, typename Number>\n    void ParabolicModule<dim, Number>::print_solver_statistics(\n        std::ostream &output) const\n    {\n      output << \"        [ \" << std::setprecision(2) << std::fixed\n             << n_iterations_velocity_\n             << (use_gmg_velocity_ ? \" GMG vel -- \" : \" CG vel -- \")\n             << n_iterations_internal_energy_\n             << (use_gmg_internal_energy_ ? \" GMG int ]\" : \" CG int ]\")\n             << std::endl;\n    }\n\n  } // namespace NavierStokes\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/navier_stokes/parabolic_module_gmg_operators.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <loop.h>\n#include <observer_pointer.h>\n#include <offline_data.h>\n#include <simd.h>\n\n#include \"../euler/hyperbolic_system.h\"\n#include \"parabolic_system.h\"\n\n#include <deal.II/base/vectorization.h>\n#include <deal.II/lac/diagonal_matrix.h>\n#include <deal.II/matrix_free/fe_evaluation.h>\n#include <deal.II/matrix_free/tools.h>\n#include <deal.II/multigrid/mg_base.h>\n#include <deal.II/multigrid/mg_transfer_matrix_free.h>\n\n/*\n * FIXME: generalize and make these operators equation independent and\n * refactor into ../parabolic_module_gmg_operators.h\n */\n\nnamespace ryujin\n{\n  namespace NavierStokes\n  {\n    using HyperbolicSystem = Euler::HyperbolicSystem;\n\n    /**\n     * A diagonal matrix used as a preconditioner for the non-multigrid CG\n     * iteration. The diagonal matrix is constructed by computing\n     * \\f$d_i=1/(\\rho_i m_i)\\f$ for a given lumped mass matrix \\f$ m_i \\f$,\n     * and a density field \\f$ \\rho_i \\f$.\n     *\n     * @ingroup NavierStokesEquations\n     */\n    template <int dim, typename Number>\n    class DiagonalMatrix\n    {\n    public:\n      /**\n       * @copydoc OfflineData::vector_type\n       */\n      using vector_type = dealii::LinearAlgebra::distributed::Vector<Number>;\n\n      /**\n       * @copydoc ParabolicModule::vector_type\n       */\n      using block_vector_type =\n          dealii::LinearAlgebra::distributed::BlockVector<Number>;\n\n      /**\n       * Constructor\n       */\n      DiagonalMatrix() = default;\n\n      /**\n       * Compute the inverse of a given density \\f$ \\rho_i \\f$ and a given\n       * lumped mass matrix \\f$ m_i \\f$, viz., \\f$d_i=1/(\\rho_i m_i)\\f$.\n       */\n      template <typename Vector>\n      void reinit(const Vector &lumped_mass_matrix,\n                  const vector_type &density,\n                  const dealii::AffineConstraints<Number> &affine_constraints)\n      {\n        diagonal.reinit(density, true);\n\n        const auto n_owned = density.get_partitioner()->locally_owned_size();\n\n        const auto body_invert = [&](auto sentinel, const unsigned int i) {\n          using T = decltype(sentinel);\n\n          T m_i;\n          if constexpr (std::is_same_v<Vector, vector_type>) {\n            m_i = read_entry<T>(lumped_mass_matrix, i);\n          } else {\n            m_i = lumped_mass_matrix.template read_entry<T>(i);\n          }\n\n          const auto rho_i = read_entry<T>(density, i);\n          write_entry<T>(diagonal, Number(1.0) / (rho_i * m_i), i);\n        };\n\n        cpu_simd_loop<Number>(\"\", body_invert, 0, n_owned, n_owned);\n\n        /*\n         * Fix up diagonal entries for constrained degrees of freedom due to\n         * periodic boundary conditions.\n         */\n        affine_constraints.set_zero(diagonal);\n      }\n\n      /**\n       * Get access to the internal vector to be externally filled.\n       */\n      vector_type &get_vector()\n      {\n        return diagonal;\n      }\n\n      /**\n       * Get access to the internal vector to be externally filled.\n       */\n      block_vector_type &get_block_vector()\n      {\n        return diagonal_block;\n      }\n\n      /**\n       * Apply on a vector.\n       */\n      void vmult(vector_type &dst, const vector_type &src) const\n      {\n        AssertDimension(diagonal_block.size(), 0);\n        DEAL_II_OPENMP_SIMD_PRAGMA\n        for (unsigned int i = 0;\n             i < diagonal.get_partitioner()->locally_owned_size();\n             ++i)\n          dst.local_element(i) =\n              diagonal.local_element(i) * src.local_element(i);\n      }\n\n      /**\n       * Apply on a block vector.\n       */\n      void vmult(block_vector_type &dst, const block_vector_type &src) const\n      {\n        AssertDimension(dim, dst.n_blocks());\n        AssertDimension(dim, src.n_blocks());\n        if (diagonal_block.size() == 0) {\n          DEAL_II_OPENMP_SIMD_PRAGMA\n          for (unsigned int i = 0;\n               i < diagonal.get_partitioner()->locally_owned_size();\n               ++i)\n            for (unsigned int d = 0; d < dim; ++d)\n              dst.block(d).local_element(i) =\n                  diagonal.local_element(i) * src.block(d).local_element(i);\n        } else\n          for (unsigned int d = 0; d < dim; ++d) {\n            DEAL_II_OPENMP_SIMD_PRAGMA\n            for (unsigned int i = 0;\n                 i < src.block(d).get_partitioner()->locally_owned_size();\n                 ++i)\n              dst.block(d).local_element(i) =\n                  diagonal_block.block(d).local_element(i) *\n                  src.block(d).local_element(i);\n          }\n      }\n\n    private:\n      vector_type diagonal;\n      block_vector_type diagonal_block;\n    };\n\n\n    /**\n     * An operator describing the velocity-velocity subblock of the\n     * parabolic system.\n     *\n     * @ingroup NavierStokesEquations\n     */\n    template <int dim, typename Number, typename Number2>\n    class VelocityMatrix : public dealii::EnableObserverPointer\n    {\n    public:\n      // FIXME: refactor\n      static constexpr unsigned int order_fe = 1;\n      static constexpr unsigned int order_quad = 2;\n\n      using vector_type = dealii::LinearAlgebra::distributed::Vector<Number>;\n      using block_vector_type =\n          dealii::LinearAlgebra::distributed::BlockVector<Number>;\n\n      VelocityMatrix() = default;\n\n      void initialize(\n          const ParabolicSystem &parabolic_system,\n          const OfflineData<dim, Number2> &offline_data,\n          const dealii::MatrixFree<dim, Number> &matrix_free,\n          const dealii::LinearAlgebra::distributed::Vector<Number> &density,\n          const Number theta_x_tau,\n          const unsigned int level = dealii::numbers::invalid_unsigned_int)\n      {\n        parabolic_system_ = &parabolic_system;\n        offline_data_ = &offline_data;\n        matrix_free_ = &matrix_free;\n        density_ = &density;\n        theta_x_tau_ = theta_x_tau;\n        level_ = level;\n      }\n\n      void Tvmult(block_vector_type &dst, const block_vector_type &src) const\n      {\n        vmult(dst, src);\n      }\n\n      void vmult(block_vector_type &dst, const block_vector_type &src) const\n      {\n        /* Apply action of m_i rho_i V_i: */\n\n        /* FIXME: we should really clean up this mess: */\n        const auto get_lumped_mass = [&](auto sentinel, unsigned int i) {\n          using T = decltype(sentinel);\n          if constexpr (std::is_same_v<Number, Number2>) {\n            if constexpr (std::is_same_v<Number, float>) {\n              if (level_ == dealii::numbers::invalid_unsigned_int) {\n                const auto &lumped = offline_data_->lumped_mass_matrix();\n                return lumped.template read_entry<T>(i);\n              } else {\n                const auto &level_lumped =\n                    offline_data_->level_lumped_mass_matrix()[level_];\n                return read_entry<T>(level_lumped, i);\n              }\n            } else {\n              Assert(level_ == dealii::numbers::invalid_unsigned_int,\n                     dealii::ExcInternalError());\n              const auto &lumped = offline_data_->lumped_mass_matrix();\n              return lumped.template read_entry<T>(i);\n            }\n          } else {\n            const auto &level_lumped =\n                offline_data_->level_lumped_mass_matrix()[level_];\n            return read_entry<T>(level_lumped, i);\n          }\n        };\n\n        const unsigned int n_owned =\n            dst.block(0).get_partitioner()->locally_owned_size();\n\n        const auto body_mass = [&](auto sentinel, unsigned int i) {\n          using T = decltype(sentinel);\n\n          const auto m_i = get_lumped_mass(T(), i);\n\n          const auto rho_i = read_entry<T>(*density_, i);\n          for (unsigned int d = 0; d < dim; ++d) {\n            const auto temp = read_entry<T>(src.block(d), i);\n            write_entry<T>(dst.block(d), m_i * rho_i * temp, i);\n          }\n        };\n\n        cpu_simd_loop<Number>(\"\", body_mass, 0, n_owned, n_owned);\n\n        /* Apply action of stress tensor: + theta * \\sum_j B_ij V_j: */\n\n        const auto integrator = [this](const auto &data,\n                                       auto &dst,\n                                       const auto &src,\n                                       const auto range) {\n          dealii::FEEvaluation<dim, order_fe, order_quad, dim, Number> velocity(\n              data);\n\n          for (unsigned int cell = range.first; cell < range.second; ++cell) {\n            velocity.reinit(cell);\n            velocity.read_dof_values(src);\n            apply_local_operator(velocity);\n            velocity.distribute_local_to_global(dst);\n          }\n        };\n\n        matrix_free_->template cell_loop<block_vector_type, block_vector_type>(\n            integrator, dst, src, /* zero destination */ false);\n\n        /* (5.4a) Fix up constrained degrees of freedom: */\n\n        const auto &boundary_map =\n            level_ == dealii::numbers::invalid_unsigned_int\n                ? offline_data_->boundary_map()\n                : offline_data_->level_boundary_map()[level_];\n\n        for (auto entry : boundary_map) {\n          // [i, normal, normal_mass, boundary_mass, id, position] = entry\n          const auto i = std::get<0>(entry);\n          if (i >= n_owned)\n            continue;\n\n          const dealii::Tensor<1, dim, Number> normal = std::get<1>(entry);\n          const auto id = std::get<4>(entry);\n\n          if (id == Boundary::slip) {\n            dealii::Tensor<1, dim, Number> V_i;\n            for (unsigned int d = 0; d < dim; ++d)\n              V_i[d] = dst.block(d).local_element(i);\n\n            /* replace normal component by source */\n            V_i -= 1. * (V_i * normal) * normal;\n            for (unsigned int d = 0; d < dim; ++d) {\n              const auto src_d = src.block(d).local_element(i);\n              V_i += 1. * (src_d * normal[d]) * normal;\n            }\n\n            for (unsigned int d = 0; d < dim; ++d)\n              dst.block(d).local_element(i) = V_i[d];\n\n          } else if (id == Boundary::no_slip || id == Boundary::dirichlet) {\n\n            /* set dst to src vector: */\n            for (unsigned int d = 0; d < dim; ++d)\n              dst.block(d).local_element(i) = src.block(d).local_element(i);\n          }\n        }\n      }\n\n      void compute_diagonal(\n          std::shared_ptr<DiagonalMatrix<dim, Number>> &matrix) const\n      {\n        Assert(level_ != dealii::numbers::invalid_unsigned_int,\n               dealii::ExcNotImplemented());\n        matrix = std::make_shared<DiagonalMatrix<dim, Number>>();\n        block_vector_type &vector = matrix->get_block_vector();\n        vector.reinit(dim);\n        for (unsigned int d = 0; d < dim; ++d)\n          matrix_free_->initialize_dof_vector(vector.block(d));\n        vector.collect_sizes();\n\n        dealii::MatrixFreeTools::compute_diagonal(\n            *matrix_free_,\n            vector,\n            &VelocityMatrix::template apply_local_operator<\n                dealii::FEEvaluation<dim, -1, 0, dim, Number>>,\n            this);\n\n        const auto &lumped_mass_matrix =\n            offline_data_->level_lumped_mass_matrix()[level_];\n        const unsigned int n_owned =\n            lumped_mass_matrix.get_partitioner()->locally_owned_size();\n\n        const auto body_invert = [&](auto sentinel, const unsigned int i) {\n          using T = decltype(sentinel);\n          const auto m_i = lumped_mass_matrix.local_element(i);\n          const auto rho_i = density_->local_element(i);\n          for (unsigned int d = 0; d < dim; ++d)\n            write_entry(vector.block(d),\n                        Number(1.) /\n                            (m_i * rho_i + read_entry<T>(vector.block(d), i)),\n                        i);\n        };\n\n        cpu_simd_loop<Number>(\"\", body_invert, 0, n_owned, n_owned);\n\n        const auto &boundary_map = offline_data_->level_boundary_map()[level_];\n\n        for (auto entry : boundary_map) {\n          // [i, normal, normal_mass, boundary_mass, id, position] = entry\n          const auto i = std::get<0>(entry);\n          if (i >= n_owned)\n            continue;\n\n          const dealii::Tensor<1, dim, Number> normal = std::get<1>(entry);\n          const auto id = std::get<4>(entry);\n\n          if (id == Boundary::slip) {\n            dealii::Tensor<1, dim, Number> V_i;\n            for (unsigned int d = 0; d < dim; ++d)\n              V_i[d] = vector.block(d).local_element(i);\n\n            /* replace normal component by 1. */\n            V_i -= 1. * (V_i * normal) * normal;\n            for (unsigned int d = 0; d < dim; ++d) {\n              V_i += 1. * (1. * normal[d]) * normal;\n            }\n\n            for (unsigned int d = 0; d < dim; ++d)\n              vector.block(d).local_element(i) = V_i[d];\n\n          } else if (id == Boundary::no_slip || id == Boundary::dirichlet) {\n\n            /* set dst to src vector: */\n            for (unsigned int d = 0; d < dim; ++d)\n              vector.block(d).local_element(i) = 1.;\n          }\n        }\n      }\n\n    private:\n      const ParabolicSystem *parabolic_system_;\n      const OfflineData<dim, Number2> *offline_data_;\n      const dealii::MatrixFree<dim, Number> *matrix_free_;\n      const vector_type *density_;\n      Number theta_x_tau_;\n      unsigned int level_;\n\n      template <typename Evaluator>\n      void apply_local_operator(Evaluator &velocity) const\n      {\n        const Number mu = parabolic_system_->mu();\n        const Number lambda = parabolic_system_->lambda();\n\n        velocity.evaluate(dealii::EvaluationFlags::gradients);\n\n        for (const unsigned int q : velocity.quadrature_point_indices()) {\n          if constexpr (dim == 1) {\n            /* Workaround: no symmetric gradient for dim == 1: */\n            const auto gradient = velocity.get_gradient(q);\n            auto S = (4. / 3. * mu + lambda) * gradient;\n            velocity.submit_gradient(theta_x_tau_ * S, q);\n\n          } else {\n\n            const auto symmetric_gradient = velocity.get_symmetric_gradient(q);\n            const auto divergence = trace(symmetric_gradient);\n            // S = (2 mu nabla^S(v) + (lambda - 2/3*mu) div(v) Id) : nabla phi\n            auto S = 2. * mu * symmetric_gradient;\n            for (unsigned int d = 0; d < dim; ++d)\n              S[d][d] += (lambda - 2. / 3. * mu) * divergence;\n            velocity.submit_symmetric_gradient(theta_x_tau_ * S, q);\n          }\n        }\n\n        velocity.integrate(dealii::EvaluationFlags::gradients);\n      }\n    };\n\n\n    /**\n     * @ingroup NavierStokesEquations\n     */\n    template <int dim, typename Number>\n    class MGTransferVelocity\n        : public dealii::MGTransferBase<\n              dealii::LinearAlgebra::distributed::BlockVector<Number>>\n    {\n    public:\n      using scalar_type = dealii::LinearAlgebra::distributed::Vector<Number>;\n      using vector_type =\n          dealii::LinearAlgebra::distributed::BlockVector<Number>;\n\n      MGTransferVelocity() = default;\n\n      void build(const dealii::DoFHandler<dim> &dof_handler,\n                 const dealii::MGConstrainedDoFs &mg_constrained_dofs,\n                 const dealii::MGLevelObject<dealii::MatrixFree<dim, Number>>\n                     &matrix_free)\n      {\n        transfer_.initialize_constraints(mg_constrained_dofs);\n        transfer_.build(dof_handler);\n        level_matrix_free_ = &matrix_free;\n        scalar_vector.resize(matrix_free.min_level(), matrix_free.max_level());\n        for (unsigned int level = matrix_free.min_level();\n             level < matrix_free.max_level();\n             ++level)\n          matrix_free[level].initialize_dof_vector(scalar_vector[level]);\n      }\n\n      void prolongate(const unsigned int to_level,\n                      vector_type &dst,\n                      const vector_type &src) const override\n      {\n        for (unsigned int block = 0; block < src.n_blocks(); ++block)\n          transfer_.prolongate(to_level, dst.block(block), src.block(block));\n      }\n\n      void restrict_and_add(const unsigned int to_level,\n                            vector_type &dst,\n                            const vector_type &src) const override\n      {\n        for (unsigned int block = 0; block < src.n_blocks(); ++block)\n          transfer_.restrict_and_add(\n              to_level, dst.block(block), src.block(block));\n      }\n\n      template <typename Number2>\n      void interpolate_to_mg(\n          const dealii::DoFHandler<dim> &dof_handler,\n          dealii::MGLevelObject<scalar_type> &dst,\n          const dealii::LinearAlgebra::distributed::Vector<Number2> &src) const\n      {\n        if (dst[dst.min_level()].size() == 0)\n          for (unsigned int l = dst.min_level(); l <= dst.max_level(); ++l)\n            (*level_matrix_free_)[l].initialize_dof_vector(dst[l]);\n        transfer_.interpolate_to_mg(dof_handler, dst, src);\n      }\n\n      template <typename Number2>\n      void\n      copy_to_mg(const dealii::DoFHandler<dim> &dof_handler,\n                 dealii::MGLevelObject<vector_type> &dst,\n                 const dealii::LinearAlgebra::distributed::BlockVector<Number2>\n                     &src) const\n      {\n        if (dst[dst.min_level()].size() == 0)\n          for (unsigned int l = dst.min_level(); l <= dst.max_level(); ++l) {\n            dst[l].reinit(src.n_blocks());\n            for (unsigned int block = 0; block < src.n_blocks(); ++block)\n              (*level_matrix_free_)[l].initialize_dof_vector(\n                  dst[l].block(block));\n            dst[l].collect_sizes();\n          }\n\n        for (unsigned int block = 0; block < src.n_blocks(); ++block) {\n          transfer_.copy_to_mg(dof_handler, scalar_vector, src.block(block));\n          for (unsigned int level = dst.min_level(); level <= dst.max_level();\n               ++level)\n            dst[level].block(block).copy_locally_owned_data_from(\n                scalar_vector[level]);\n        }\n      }\n\n      template <typename Number2>\n      void copy_from_mg(\n          const dealii::DoFHandler<dim> &dof_handler,\n          dealii::LinearAlgebra::distributed::BlockVector<Number2> &dst,\n          const dealii::MGLevelObject<vector_type> &src) const\n      {\n        for (unsigned int block = 0; block < dst.n_blocks(); ++block) {\n          for (unsigned int level = src.min_level(); level <= src.max_level();\n               ++level)\n            scalar_vector[level].copy_locally_owned_data_from(\n                src[level].block(block));\n          transfer_.copy_from_mg(dof_handler, dst.block(block), scalar_vector);\n        }\n      }\n\n    private:\n      dealii::MGTransferMatrixFree<dim, Number> transfer_;\n      const dealii::MGLevelObject<dealii::MatrixFree<dim, Number>>\n          *level_matrix_free_;\n      mutable dealii::MGLevelObject<scalar_type> scalar_vector;\n    };\n\n\n    /**\n     * @ingroup NavierStokesEquations\n     */\n    template <int dim, typename Number, typename Number2>\n    class EnergyMatrix : public dealii::EnableObserverPointer\n    {\n    public:\n      // FIXME: refactor\n      static constexpr unsigned int order_fe = 1;\n      static constexpr unsigned int order_quad = 2;\n\n      using vector_type = dealii::LinearAlgebra::distributed::Vector<Number>;\n\n      EnergyMatrix() = default;\n\n      void initialize(\n          const OfflineData<dim, Number2> &offline_data,\n          const dealii::MatrixFree<dim, Number> &matrix_free,\n          const dealii::LinearAlgebra::distributed::Vector<Number> &density,\n          const Number time_factor,\n          const unsigned int level = dealii::numbers::invalid_unsigned_int)\n      {\n        offline_data_ = &offline_data;\n        matrix_free_ = &matrix_free;\n        density_ = &density;\n        factor_ = time_factor;\n        level_ = level;\n      }\n\n      void Tvmult(vector_type &dst, const vector_type &src) const\n      {\n        vmult(dst, src);\n      }\n\n      dealii::types::global_dof_index m() const\n      {\n        return density_->size();\n      }\n\n      Number el(const unsigned int, const unsigned int) const\n      {\n        Assert(false, dealii::ExcNotImplemented());\n        return Number();\n      }\n\n      void vmult(vector_type &dst, const vector_type &src) const\n      {\n        /* Apply action of m_i rho_i V_i: */\n\n        /* FIXME: we should really clean up this mess: */\n        const auto get_lumped_mass = [&](auto sentinel, unsigned int i) {\n          using T = decltype(sentinel);\n          if constexpr (std::is_same_v<Number, Number2>) {\n            if constexpr (std::is_same_v<Number, float>) {\n              if (level_ == dealii::numbers::invalid_unsigned_int) {\n                const auto &lumped = offline_data_->lumped_mass_matrix();\n                return lumped.template read_entry<T>(i);\n              } else {\n                const auto &level_lumped =\n                    offline_data_->level_lumped_mass_matrix()[level_];\n                return read_entry<T>(level_lumped, i);\n              }\n            } else {\n              Assert(level_ == dealii::numbers::invalid_unsigned_int,\n                     dealii::ExcInternalError());\n              const auto &lumped = offline_data_->lumped_mass_matrix();\n              return lumped.template read_entry<T>(i);\n            }\n          } else {\n            const auto &level_lumped =\n                offline_data_->level_lumped_mass_matrix()[level_];\n            return read_entry<T>(level_lumped, i);\n          }\n        };\n\n        const unsigned int n_owned =\n            dst.get_partitioner()->locally_owned_size();\n\n        const auto body_mass = [&](auto sentinel, unsigned int i) {\n          using T = decltype(sentinel);\n          const auto m_i = get_lumped_mass(T(), i);\n          const auto rho_i = read_entry<T>(*density_, i);\n          const auto e_i = read_entry<T>(src, i);\n          write_entry<T>(dst, m_i * rho_i * e_i, i);\n        };\n\n        cpu_simd_loop<Number>(\"\", body_mass, 0, n_owned, n_owned);\n\n        /* Apply action of diffusion operator \\sum_j beta_ij e_j: */\n\n        const auto integrator = [this](const auto &data,\n                                       auto &dst,\n                                       const auto &src,\n                                       const auto range) {\n          dealii::FEEvaluation<dim, order_fe, order_quad, 1, Number> energy(\n              data);\n\n          for (unsigned int cell = range.first; cell < range.second; ++cell) {\n            energy.reinit(cell);\n            energy.read_dof_values(src);\n            apply_local_operator(energy);\n            energy.distribute_local_to_global(dst);\n          }\n        };\n\n        matrix_free_->template cell_loop<vector_type, vector_type>(\n            integrator, dst, src, /* zero destination */ false);\n\n        /* Fix up constrained degrees of freedom: */\n\n        const auto &boundary_map =\n            (level_ == dealii::numbers::invalid_unsigned_int)\n                ? offline_data_->boundary_map()\n                : offline_data_->level_boundary_map()[level_];\n\n        for (auto entry : boundary_map) {\n          const auto i = std::get<0>(entry);\n          if (i >= n_owned)\n            continue;\n\n          const auto id = std::get<4>(entry);\n          if (id == Boundary::dirichlet)\n            dst.local_element(i) = src.local_element(i);\n        }\n      }\n\n      void compute_diagonal(\n          std::shared_ptr<dealii::DiagonalMatrix<vector_type>> &matrix) const\n      {\n        Assert(level_ != dealii::numbers::invalid_unsigned_int,\n               dealii::ExcNotImplemented());\n        matrix = std::make_shared<dealii::DiagonalMatrix<vector_type>>();\n        vector_type &vector = matrix->get_vector();\n        matrix_free_->initialize_dof_vector(vector);\n\n        const vector_type &lumped_mass_matrix =\n            offline_data_->level_lumped_mass_matrix()[level_];\n\n        dealii::MatrixFreeTools::compute_diagonal(\n            *matrix_free_,\n            vector,\n            &EnergyMatrix::template apply_local_operator<\n                dealii::FEEvaluation<dim, -1, 0, dim, Number>>,\n            this);\n\n        const unsigned int n_owned =\n            lumped_mass_matrix.get_partitioner()->locally_owned_size();\n\n        const auto body_invert = [&](auto sentinel, const unsigned int i) {\n          using T = decltype(sentinel);\n\n          const auto m_i = read_entry<T>(lumped_mass_matrix, i);\n          const auto rho_i = read_entry<T>(*density_, i);\n          write_entry<T>(\n              vector, Number(1.) / (m_i * rho_i + read_entry<T>(vector, i)), i);\n        };\n        cpu_simd_loop<Number>(\"\", body_invert, 0, n_owned, n_owned);\n\n        const auto &boundary_map = offline_data_->level_boundary_map()[level_];\n\n        for (auto entry : boundary_map) {\n          const auto i = std::get<0>(entry);\n          if (i >= n_owned)\n            continue;\n\n          const auto id = std::get<4>(entry);\n          if (id == Boundary::dirichlet)\n            vector.local_element(i) = 1.;\n        }\n      }\n\n    private:\n      const OfflineData<dim, Number2> *offline_data_;\n      const dealii::MatrixFree<dim, Number> *matrix_free_;\n      const dealii::LinearAlgebra::distributed::Vector<Number> *density_;\n      Number factor_;\n      unsigned int level_;\n\n      template <typename Evaluator>\n      void apply_local_operator(Evaluator &energy) const\n      {\n        energy.evaluate(dealii::EvaluationFlags::gradients);\n        for (unsigned int q = 0; q < energy.n_q_points; ++q) {\n          energy.submit_gradient(factor_ * energy.get_gradient(q), q);\n        }\n        energy.integrate(dealii::EvaluationFlags::gradients);\n      }\n    };\n\n\n    /**\n     * @ingroup NavierStokesEquations\n     */\n    template <int dim, typename Number>\n    class MGTransferEnergy : public dealii::MGTransferMatrixFree<dim, Number>\n    {\n    public:\n      void build(const dealii::DoFHandler<dim> &dof_handler,\n                 const dealii::MGLevelObject<dealii::MatrixFree<dim, Number>>\n                     &matrix_free)\n      {\n        dealii::MGTransferMatrixFree<dim, Number>::build(dof_handler);\n        level_matrix_free_ = &matrix_free;\n      }\n\n      template <typename Number2>\n      void copy_to_mg(\n          const dealii::DoFHandler<dim> &dof_handler,\n          dealii::MGLevelObject<\n              dealii::LinearAlgebra::distributed::Vector<Number>> &dst,\n          const dealii::LinearAlgebra::distributed::Vector<Number2> &src) const\n      {\n        if (dst[dst.min_level()].size() == 0)\n          for (unsigned int l = dst.min_level(); l <= dst.max_level(); ++l)\n            (*level_matrix_free_)[l].initialize_dof_vector(dst[l]);\n        dealii::MGTransferMatrixFree<dim, Number>::copy_to_mg(\n            dof_handler, dst, src);\n      }\n\n    private:\n      const dealii::MGLevelObject<dealii::MatrixFree<dim, Number>>\n          *level_matrix_free_;\n    };\n\n  } // namespace NavierStokes\n} /* namespace ryujin */\n\n#undef locally_owned_size\n"
  },
  {
    "path": "source/navier_stokes/parabolic_system.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2022 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <convenience_macros.h>\n\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/tensor.h>\n\n#include <array>\n#include <functional>\n#include <iomanip>\n\nnamespace ryujin\n{\n  namespace NavierStokes\n  {\n    /**\n     * A Newtonian  fluid viscosity model with a heat-flux governed by\n     * Fourier's law. This class describes the parabolic system part of the\n     * combined compressible Navier-Stokes equations.\n     *\n     * @ingroup NavierStokesEquations\n     */\n    class ParabolicSystem final : public dealii::ParameterAcceptor\n    {\n    public:\n      /**\n       * The name of the hyperbolic system as a string.\n       */\n      static inline const std::string problem_name =\n          \"Newtonian fluid viscosity model with Fourier-law heat flux\";\n\n      /**\n       * This parabolic subsystem represents an identity.\n       */\n      static constexpr bool is_identity = false;\n\n      /**\n       * Constructor.\n       */\n      ParabolicSystem(const std::string &subsection = \"/B - Equation\");\n\n      ACCESSOR_READ_ONLY(mu)\n      ACCESSOR_READ_ONLY(lambda)\n      ACCESSOR_READ_ONLY(cv_inverse_kappa)\n\n      ACCESSOR_READ_ONLY(parabolic_component_names);\n\n    private:\n      /**\n       * @name Runtime parameters, internal fields and methods\n       */\n      //@{\n\n      double mu_;\n      double lambda_;\n      double cv_inverse_kappa_;\n\n      //@}\n\n      const std::vector<std::string> parabolic_component_names_;\n    }; /* ParabolicSystem */\n\n\n    /*\n     * -------------------------------------------------------------------------\n     * Inline definitions\n     * -------------------------------------------------------------------------\n     */\n\n\n    inline ParabolicSystem::ParabolicSystem(const std::string &subsection)\n        : ParameterAcceptor(subsection)\n    {\n      mu_ = 1.e-3;\n      add_parameter(\"mu\", mu_, \"The shear viscosity constant\");\n\n      lambda_ = 0.;\n      add_parameter(\"lambda\", lambda_, \"The bulk viscosity constant\");\n\n      cv_inverse_kappa_ = 1.866666666666666e-2;\n      add_parameter(\"kappa\",\n                    cv_inverse_kappa_,\n                    \"Scaled thermal conductivity constant: c_v^{-1} kappa\");\n    }\n\n  } // namespace NavierStokes\n} // namespace ryujin\n"
  },
  {
    "path": "source/newton.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2024 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"simd.h\"\n\n#include <array>\n\nnamespace ryujin\n{\n  /**\n   * @name Functions\n   */\n  //@{\n\n  /**\n   * Perform one step of a quadratic Newton iteration, see\n   * @cite ryujin-2021-1, Algorithm 3.\n   *\n   * @note For this, it has to hold true that \\f$p_1\\le p^\\ast\\le p_2\\f$,\n   * and \\f$\\phi(p_1)\\le 0\\le \\phi(p_2)\\f$, or \\f$\\phi(p_1)\\ge 0\\ge\n   * \\phi(p_2)\\f$ and that \\f$\\phi\\f$ itself is a 3-convex/concave\n   * function, i.e., \\f$\\phi'''<0\\f$, or \\f$\\phi'''>0\\f$.\n   *\n   * Modifies p_1 and P_2 ensures that p_1 <= p_2, and that p_1 (p_2) is\n   * monotonically increasing (decreasing).\n   *\n   * @todo Write out the quadratic Newton step in more detail.\n   *\n   * @ingroup Miscellenaous\n   */\n  template <typename Number>\n  DEAL_II_ALWAYS_INLINE inline void\n  quadratic_newton_step(Number &p_1,\n                        Number &p_2,\n                        const Number phi_p_1,\n                        const Number phi_p_2,\n                        const Number dphi_p_1,\n                        const Number dphi_p_2,\n                        const Number sign = Number(1.0))\n  {\n    using ScalarNumber = typename get_value_type<Number>::type;\n    constexpr ScalarNumber eps = std::numeric_limits<ScalarNumber>::epsilon();\n\n    /* Compute divided differences: */\n\n    const auto scaling = ScalarNumber(1.) / (p_2 - p_1 + Number(eps));\n\n    const Number dd_11 = dphi_p_1;\n    const Number dd_12 = (phi_p_2 - phi_p_1) * scaling;\n    const Number dd_22 = dphi_p_2;\n\n    const Number dd_112 = (dd_12 - dd_11) * scaling;\n    const Number dd_122 = (dd_22 - dd_12) * scaling;\n\n    /* Update left and right point: */\n\n    const auto discriminant_1 =\n        std::abs(dphi_p_1 * dphi_p_1 - ScalarNumber(4.) * phi_p_1 * dd_112);\n    const auto discriminant_2 =\n        std::abs(dphi_p_2 * dphi_p_2 - ScalarNumber(4.) * phi_p_2 * dd_122);\n\n    const auto denominator_1 = dphi_p_1 + sign * std::sqrt(discriminant_1);\n    const auto denominator_2 = dphi_p_2 + sign * std::sqrt(discriminant_2);\n\n    /* Make sure we do not produce NaNs: */\n\n    auto t_1 =\n        p_1 - dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(\n                  std::abs(denominator_1),\n                  Number(eps),\n                  Number(0.),\n                  ScalarNumber(2.) * phi_p_1 / denominator_1);\n\n    auto t_2 =\n        p_2 - dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(\n                  std::abs(denominator_2),\n                  Number(eps),\n                  Number(0.),\n                  ScalarNumber(2.) * phi_p_2 / denominator_2);\n\n    /* Enforce bounds: */\n\n    t_1 = std::max(p_1, t_1);\n    t_1 = std::min(p_2, t_1);\n\n    t_2 = std::max(p_1, t_2);\n    t_2 = std::min(p_2, t_2);\n\n    /* Ensure that always p_1 <= p_2: */\n\n    p_1 = std::min(t_1, t_2);\n    p_2 = std::max(t_1, t_2);\n\n    return;\n  }\n\n  //@}\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/observer_pointer.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <deal.II/base/config.h>\n\n#if DEAL_II_VERSION_GTE(9, 7, 0)\n#include <deal.II/base/enable_observer_pointer.h>\n#include <deal.II/base/observer_pointer.h>\n\n#else\n\n#include <deal.II/base/smartpointer.h>\n#include <deal.II/base/subscriptor.h>\n\nDEAL_II_NAMESPACE_OPEN\n\ntemplate <typename T, typename P = void>\nusing ObserverPointer = SmartPointer<T, P>;\n\nusing EnableObserverPointer = Subscriptor;\nDEAL_II_NAMESPACE_CLOSE\n\n#endif\n"
  },
  {
    "path": "source/offline_data.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2023 by the ryujin authors\n//\n\n#include \"offline_data.template.h\"\n\nnamespace ryujin\n{\n  /* instantiations */\n  template class OfflineData<1, NUMBER>;\n  template class OfflineData<2, NUMBER>;\n  template class OfflineData<3, NUMBER>;\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/offline_data.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"convenience_macros.h\"\n#include \"discretization.h\"\n#include \"mpi_ensemble.h\"\n#include \"observer_pointer.h\"\n#include \"sparse_matrix.h\"\n#include \"sparsity_pattern.h\"\n#include \"state_vector.h\"\n\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/partitioner.h>\n#include <deal.II/dofs/dof_handler.h>\n#include <deal.II/lac/affine_constraints.h>\n#include <deal.II/lac/la_parallel_vector.h>\n#include <deal.II/lac/sparse_matrix.h>\n#include <deal.II/numerics/data_out.h>\n\nnamespace ryujin\n{\n  /**\n   * A class to store all data that can be precomputed offline.\n   *\n   * This class takes a reference to a Discretization object (that itself\n   * holds a Triangulation, FiniteElement, Mapping, and Quadrature object).\n   *\n   * Most notably this class sets up a DoFHandler, the\n   * SparsityPattern, various IndexSet objects to hold locally\n   * owned and locally relevant indices, and precomputes all matrices (mass\n   * matrix, lumped mass matrix, $c_{ij}$ matrices, and $n_{ij}$ matrices).\n   *\n   * After @p prepare() is called, all getter functions return valid\n   * references.\n   *\n   * @note The offline data precomputed in this class is problem\n   * independent, it only depends on the chosen geometry and ansatz stored\n   * in the Discretization class.\n   *\n   * @note Data structures in OfflineData are initialized with the ensemble\n   * subrange communicator stored in MPIEnsemble.\n   *\n   * @ingroup Mesh\n   */\n  template <int dim, typename Number = double>\n  class OfflineData : public dealii::ParameterAcceptor\n  {\n  public:\n    /**\n     * A tuple describing (local) dof index, boundary normal, normal mass,\n     * boundary mass, boundary id, and position of the boundary degree of\n     * freedom.\n     */\n    using BoundaryDescription =\n        std::tuple<unsigned int /*i*/,\n                   dealii::Tensor<1, dim, Number> /*normal*/,\n                   Number /*normal mass*/,\n                   Number /*boundary mass*/,\n                   dealii::types::boundary_id /*id*/,\n                   dealii::Point<dim>> /*position*/;\n\n    /**\n     * A tuple describing coupling boundary degrees of freedom on directly\n     * enforced boundaries for which we have to symmetrize the d_ij matrix.\n     */\n    using CouplingDescription = std::tuple<unsigned int /*i*/, //\n                                           unsigned int /*col_idx*/,\n                                           unsigned int /*j*/>;\n\n    /**\n     * Constructor\n     */\n    OfflineData(const MPIEnsemble &mpi_ensemble,\n                const Discretization<dim> &discretization,\n                const std::string &subsection = \"/OfflineData\");\n\n    /**\n     * Prepare offline data. A call to prepare() sets up storage, dof\n     * handlers, sparsity patterns, and assembles all matrices.\n     *\n     * The problem_dimension and n_precomputed_values parameters is used to\n     * set up appropriately sized vector partitioners for the state and\n     * precomputed MultiComponentVector.\n     */\n    void prepare(const unsigned int problem_dimension,\n                 const unsigned int n_precomputed_values);\n\n    /**\n     * Return a read-only const reference to the DoFHandler for the\n     * continuous (\"cG\") variant of the selected finite element space.\n     *\n     * @note If the selected finite element space is continuous then this\n     * method simply returns the same object as dof_handler().\n     */\n    ACCESSOR_READ_ONLY(dof_handler_cg)\n\n    /**\n     * Return a read-only const reference to the DoFHandler for the\n     * discontinuous (\"dG\") variant of the selected finite element space.\n     *\n     * @note If the selected finite element space is discontinuous then\n     * this method simply returns the same object as dof_handler().\n     */\n    ACCESSOR_READ_ONLY(dof_handler_dg)\n\n    /**\n     * Return a read-only const reference to the dof handler.\n     */\n    const dealii::DoFHandler<dim> &dof_handler() const\n    {\n      if (discretization_->have_discontinuous_ansatz()) {\n        Assert(dof_handler_dg_, dealii::ExcInternalError());\n        return *dof_handler_dg_;\n      } else {\n        Assert(dof_handler_cg_, dealii::ExcInternalError());\n        return *dof_handler_cg_;\n      }\n    }\n\n    /**\n     * Return a writable reference to the dof handler.\n     */\n    dealii::DoFHandler<dim> &dof_handler()\n    {\n      if (discretization_->have_discontinuous_ansatz()) {\n        Assert(dof_handler_dg_, dealii::ExcInternalError());\n        return *dof_handler_dg_;\n      } else {\n        Assert(dof_handler_cg_, dealii::ExcInternalError());\n        return *dof_handler_cg_;\n      }\n    }\n\n    /**\n     * An AffineConstraints object storing constraints for the continuous\n     * (\"cG\") variant of the selected finite element space in (deal.II\n     * typical) global numbering.\n     *\n     * @note The affine constraints object is populated with with (a)\n     * hanging node constraints, and (b) periodicity constraints, that\n     * directly affect the chosen ansatz space.\n     *\n     * @note If the selected finite element space is continuous then this\n     * method simply returns the same object as affine_constraints().\n     */\n    ACCESSOR_READ_ONLY(affine_constraints_cg)\n\n    /**\n     * An AffineConstraints object storing constraints for the\n     * discontinuous (\"dG\") variant of the selected finite element space in\n     * (deal.II typical) global numbering.\n     *\n     * @note The affine constraints object is populated with with (a)\n     * hanging node constraints, and (b) periodicity constraints, that\n     * directly affect the chosen ansatz space.\n     *\n     * @note If the selected finite element space is discontinuous then\n     * this method simply returns the same object as affine_constraints().\n     */\n    ACCESSOR_READ_ONLY(affine_constraints_dg)\n\n    /**\n     * An AffineConstraints object storing constraints in (deal.II typical)\n     * global numbering.\n     *\n     * @note The affine constraints object is populated with with (a)\n     * hanging node constraints, and (b) periodicity constraints, that\n     * directly affect the chosen ansatz space.\n     */\n    const dealii::AffineConstraints<Number> &affine_constraints() const\n    {\n      if (discretization_->have_discontinuous_ansatz()) {\n        return affine_constraints_dg_;\n      } else {\n        return affine_constraints_cg_;\n      }\n    }\n\n    /**\n     * An MPI partitioner for the (scalar) Vector storing a scalar-valued\n     * quantity.\n     */\n    ACCESSOR_READ_ONLY_NO_DEREFERENCE(scalar_partitioner)\n\n    /**\n     * An MPI partitioner for the MultiComponentVector storing a\n     * vector-valued quantity of size HyperbolicSystem::problem_dimension.\n     */\n    ACCESSOR_READ_ONLY_NO_DEREFERENCE(hyperbolic_vector_partitioner)\n\n    /**\n     * An MPI partitioner for the MultiComponentVector storing a\n     * vector-valued quantity of size HyperbolicSystem::problem_dimension.\n     */\n    ACCESSOR_READ_ONLY_NO_DEREFERENCE(precomputed_vector_partitioner)\n\n    /**\n     * The subinterval \\f$[0,\\texttt{n_export_indices()})\\f$ contains all\n     * (SIMD-vectorized) indices of the interval\n     * \\f$[0,\\texttt{n_locally_internal()})\\f$ that are exported to\n     * neighboring MPI ranks.\n     *\n     * @note The interval \\f$[\\texttt{n_locally_internal()},\n     * \\texttt{n_locally_relevant()})\\f$ (consisting of non-SIMD-vectorized\n     * indices) contains additional degrees of freedom that might have to\n     * be exported to neighboring MPI ranks.\n     */\n    ACCESSOR_READ_ONLY(n_export_indices)\n\n    /**\n     * Number of locally owned internal degrees of freedom: In (MPI rank)\n     * local numbering all indices in the half open interval [0,\n     * n_locally_internal_) are owned by this processor, have standard\n     * connectivity, and are not situated at a boundary.\n     */\n    ACCESSOR_READ_ONLY(n_locally_internal)\n\n    /**\n     * Number of locally owned degrees of freedom: In (MPI rank) local\n     * numbering all indices in the half open interval [0,\n     * n_locally_owned_) are owned by this processor.\n     */\n    ACCESSOR_READ_ONLY(n_locally_owned)\n\n    /**\n     * Number of locally relevant degrees of freedom: This number is the\n     * toal number of degrees of freedom we store locally on this MPI rank.\n     * I.e.,  we can access the half open interval [0, n_locally_relevant_)\n     * on this machine.\n     */\n    ACCESSOR_READ_ONLY(n_locally_relevant)\n\n    /**\n     * The boundary map. Local numbering.\n     *\n     * For every degree of freedom that has nonzero support at the boundary\n     * we record the global degree of freedom index along with a weighted\n     * boundary normal, the associated boundary id, and position.\n     *\n     * This map is later used in OfflineData to handle boundary\n     * degrees of freedom after every time step (for example to implement\n     * reflective boundary conditions).\n     */\n    ACCESSOR_READ_ONLY(boundary_map)\n\n    /**\n     * A vector of tuples describing coupling degrees of freedom i and j\n     * where both degrees of freedom are collocated at the boundary (and\n     * hence the d_ij matrix has to be symmetrized). The function returns a\n     * reference to a vector of tuples consisting of (i, col_idx, j).\n     */\n    ACCESSOR_READ_ONLY(coupling_boundary_pairs)\n\n    /**\n     * The boundary map on all levels of the grid in case multilevel\n     * support was enabled.\n     */\n    ACCESSOR_READ_ONLY(level_boundary_map)\n\n    /**\n     * A sparsity pattern for (standard deal.II) matrices storing indices\n     * in (deal.II typical) global numbering.\n     */\n    ACCESSOR_READ_ONLY(sparsity_pattern)\n\n    /**\n     * A sparsity pattern for matrices in vectorized format. Local\n     * numbering.\n     */\n    ACCESSOR_READ_ONLY(sparsity_pattern_simd)\n\n    /**\n     * The mass matrix. (SIMD storage, local numbering)\n     */\n    ACCESSOR_READ_ONLY(mass_matrix)\n\n    /**\n     * The inverse mass matrix. (SIMD storage, local numbering)\n     *\n     * This matrix is only available for a discontinuous finite Element\n     * ansatz.\n     */\n    ACCESSOR_READ_ONLY(mass_matrix_inverse)\n\n    /**\n     * The lumped mass matrix. (stored as vector, local numbering)\n     */\n    ACCESSOR_READ_ONLY(lumped_mass_matrix)\n\n    /**\n     * The inverse of the lumped mass matrix. (stored as vector, local\n     * numbering)\n     */\n    ACCESSOR_READ_ONLY(lumped_mass_matrix_inverse)\n\n    /**\n     * The lumped mass matrix on all levels of the grid in case multilevel\n     * support was enabled.\n     */\n    ACCESSOR_READ_ONLY(level_lumped_mass_matrix)\n\n    /**\n     * The stiffness matrix \\f$(beta_{ij})\\f$:\n     *   \\f$\\beta_{ij} = \\nabla\\varphi_{j}\\cdot\\nabla\\varphi_{i}\\f$\n     * (SIMD storage, local numbering)\n     */\n    ACCESSOR_READ_ONLY(betaij_matrix)\n\n    /**\n     * The \\f$(c_{ij})\\f$ matrix. (SIMD storage, local numbering)\n     */\n    ACCESSOR_READ_ONLY(cij_matrix)\n\n    /**\n     * The incidence matrix \\f$(beta_{ij})\\f$: 1 for coupling face degrees\n     * of freedom that share the same support point coordinate, 0 otherwise.\n     *\n     * (SIMD storage, local numbering)\n     *\n     * This matrix is only available for a discontinuous finite Element\n     * ansatz.\n     */\n    ACCESSOR_READ_ONLY(incidence_matrix)\n\n    /**\n     * Size of computational domain.\n     */\n    ACCESSOR_READ_ONLY(measure_of_omega)\n\n    /**\n     * Returns a reference of the underlying Discretization object.\n     */\n    ACCESSOR_READ_ONLY(discretization)\n\n  private:\n    /**\n     * Private methods used in prepare()\n     */\n    //@{\n\n    /**\n     * Set up DoFHandlers. Internally used in prepare().\n     *\n     * @note This method populates various OfflineData internal data structures.\n     */\n    void create_dof_handlers();\n\n    /**\n     * Renumber the hyperbolic DoFHandler for use with our SIMD sparsity\n     * pattern. Internally used in prepare().\n     *\n     * @note This method populates various OfflineData internal data structures.\n     */\n    void renumber_for_simd();\n\n    /**\n     * Set up affine constraints and sparsity pattern. Internally used in\n     * setup().\n     *\n     * @note This method populates various OfflineData internal data structures.\n     */\n    void create_constraints_and_sparsity_pattern();\n\n    /**\n     *\n     * @note This method populates various OfflineData internal data structures.\n     */\n    void ensure_simd_stride_consistency();\n\n    /**\n     * Create partitioner. Internally used in setup().\n     *\n     * @note This method populates various OfflineData internal data structures.\n     */\n    void create_partitioner_and_simd_sparsity(\n        const unsigned int problem_dimension,\n        const unsigned int n_precomputed_values);\n\n    /**\n     * Assemble all matrices. Internally used in prepare().\n     *\n     * @note This method populates various OfflineData internal data structures.\n     */\n    void create_matrices();\n\n    /**\n     * Create multigrid data. Internally used in prepare().\n     *\n     * @note This method populates various OfflineData internal data structures.\n     */\n    void create_multigrid_data();\n\n    //@}\n    /**\n     * Private fields\n     */\n    //@{\n\n    const MPIEnsemble &mpi_ensemble_;\n\n    dealii::ObserverPointer<const Discretization<dim>> discretization_;\n\n    std::unique_ptr<dealii::DoFHandler<dim>> dof_handler_cg_;\n    std::unique_ptr<dealii::DoFHandler<dim>> dof_handler_dg_;\n\n    dealii::AffineConstraints<Number> affine_constraints_cg_;\n    dealii::AffineConstraints<Number> affine_constraints_dg_;\n\n    std::shared_ptr<const dealii::Utilities::MPI::Partitioner>\n        scalar_partitioner_;\n\n    std::shared_ptr<const dealii::Utilities::MPI::Partitioner>\n        hyperbolic_vector_partitioner_;\n\n    std::shared_ptr<const dealii::Utilities::MPI::Partitioner>\n        precomputed_vector_partitioner_;\n\n    unsigned int n_export_indices_;\n    unsigned int n_locally_internal_;\n    unsigned int n_locally_owned_;\n    unsigned int n_locally_relevant_;\n\n    using BoundaryMap = std::vector<BoundaryDescription>;\n    BoundaryMap boundary_map_;\n    std::vector<BoundaryMap> level_boundary_map_;\n\n    using CouplingBoundaryPairs = std::vector<CouplingDescription>;\n    CouplingBoundaryPairs coupling_boundary_pairs_;\n\n    dealii::DynamicSparsityPattern sparsity_pattern_;\n\n    SparsityPattern<dealii::VectorizedArray<Number>::size()>\n        sparsity_pattern_simd_;\n\n    SparseMatrix<Number> mass_matrix_;\n    SparseMatrix<Number> mass_matrix_inverse_;\n\n    using ScalarVector = Vectors::ScalarVector<Number>;\n    ScalarVector lumped_mass_matrix_;\n    ScalarVector lumped_mass_matrix_inverse_;\n\n    using ScalarHostVectorFloat = Vectors::ScalarHostVector<float>;\n    std::vector<ScalarHostVectorFloat> level_lumped_mass_matrix_;\n\n    SparseMatrix<Number> betaij_matrix_;\n    SparseMatrix<Number, dim> cij_matrix_;\n    SparseMatrix<Number> incidence_matrix_;\n\n    Number measure_of_omega_;\n\n    /**\n     * Construct a boundary map for a given set of DoFHandler iterators.\n     */\n    template <typename ITERATOR1, typename ITERATOR2>\n    BoundaryMap construct_boundary_map(\n        const ITERATOR1 &begin,\n        const ITERATOR2 &end,\n        const dealii::Utilities::MPI::Partitioner &partitioner) const;\n\n    /**\n     * Collect coupling pairs of locally owned (and locally relevant)\n     * boundary degrees of freedom.\n     */\n    template <typename ITERATOR1, typename ITERATOR2>\n    CouplingBoundaryPairs collect_coupling_boundary_pairs(\n        const ITERATOR1 &begin,\n        const ITERATOR2 &end,\n        const dealii::Utilities::MPI::Partitioner &partitioner) const;\n\n    //@}\n    /**\n     * @name Run time options\n     */\n    //@{\n\n    bool treat_fe_nothing_as_boundary_;\n    double incidence_relaxation_even_;\n    double incidence_relaxation_odd_;\n\n    //@}\n  };\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/offline_data.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include \"discretization.h\"\n#include \"local_index_handling.h\"\n#include \"loop.h\"\n#include \"multicomponent_vector.h\"\n#include \"offline_data.h\"\n#include \"scratch_data.h\"\n#include \"simd.h\"\n\n#include <deal.II/base/graph_coloring.h>\n#include <deal.II/base/parallel.h>\n#include <deal.II/base/work_stream.h>\n#include <deal.II/dofs/dof_renumbering.h>\n#include <deal.II/dofs/dof_tools.h>\n#include <deal.II/fe/fe_nothing.h>\n#include <deal.II/fe/fe_values.h>\n#include <deal.II/grid/grid_tools.h>\n#include <deal.II/lac/dynamic_sparsity_pattern.h>\n#include <deal.II/lac/la_parallel_vector.h>\n\nnamespace ryujin\n{\n  using namespace dealii;\n\n\n  template <int dim, typename Number>\n  OfflineData<dim, Number>::OfflineData(\n      const MPIEnsemble &mpi_ensemble,\n      const Discretization<dim> &discretization,\n      const std::string &subsection /*= \"OfflineData\"*/)\n      : ParameterAcceptor(subsection)\n      , mpi_ensemble_(mpi_ensemble)\n      , discretization_(&discretization)\n  {\n    treat_fe_nothing_as_boundary_ = true;\n    add_parameter(\n        \"treat fe_nothing as boundary\",\n        treat_fe_nothing_as_boundary_,\n        \"If set to true, we treat cell with where the active finite element is \"\n        \"set to FE_Nothing as boundary: We do not assemble an interior jump \"\n        \"over such elements and add vertices touching such an element in the \"\n        \"boundary map. In this case, the boundary id of the interior face is \"\n        \"set via the material id of the cell with active FE_Nothing element.\");\n\n    incidence_relaxation_even_ = 0.5;\n    add_parameter(\"incidence matrix relaxation even degree\",\n                  incidence_relaxation_even_,\n                  \"Scaling exponent for incidence matrix used for \"\n                  \"discontinuous finite elements with even degree. The default \"\n                  \"value 0.5 scales the jump penalization with (h_i+h_j)^0.5.\");\n\n    incidence_relaxation_odd_ = 0.0;\n    add_parameter(\"incidence matrix relaxation odd degree\",\n                  incidence_relaxation_odd_,\n                  \"Scaling exponent for incidence matrix used for \"\n                  \"discontinuous finite elements with even degree. The default \"\n                  \"value of 0.0 sets the jump penalization to a constant 1.\");\n  }\n\n\n  template <int dim, typename Number>\n  void\n  OfflineData<dim, Number>::prepare(const unsigned int problem_dimension,\n                                    const unsigned int n_precomputed_values)\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"OfflineData<dim, Number>::prepare()\" << std::endl;\n#endif\n\n    create_dof_handlers();\n\n    renumber_for_simd();\n\n    create_constraints_and_sparsity_pattern();\n\n    ensure_simd_stride_consistency();\n\n    create_partitioner_and_simd_sparsity(problem_dimension,\n                                         n_precomputed_values);\n\n    create_matrices();\n\n    if (!dof_handler().has_hp_capabilities())\n      create_multigrid_data();\n  }\n\n\n  template <int dim, typename Number>\n  void OfflineData<dim, Number>::create_dof_handlers()\n  {\n    const auto &triangulation = discretization_->triangulation();\n\n    if (!dof_handler_cg_)\n      dof_handler_cg_ =\n          std::make_unique<dealii::DoFHandler<dim>>(triangulation);\n\n    if (!dof_handler_dg_)\n      dof_handler_dg_ =\n          std::make_unique<dealii::DoFHandler<dim>>(triangulation);\n\n    /*\n     * Set active FE indices: This information depends on the selected\n     * geometry. Therefore, let the selected geometry object handle the\n     * setup. For a standard geometry that has only one reference element\n     * the method simply does nothing.\n     */\n\n    discretization_->selected_geometry().update_dof_handler(*dof_handler_cg_);\n    discretization_->selected_geometry().update_dof_handler(*dof_handler_dg_);\n\n    dof_handler_cg_->distribute_dofs(discretization_->finite_element_cg());\n    dof_handler_dg_->distribute_dofs(discretization_->finite_element_dg());\n  }\n\n\n  template <int dim, typename Number>\n  void OfflineData<dim, Number>::renumber_for_simd()\n  {\n    auto &dof_handler = this->dof_handler();\n    const IndexSet &locally_owned = dof_handler.locally_owned_dofs();\n    n_locally_owned_ = locally_owned.n_elements();\n\n    /*\n     * Initial renumbering with Cuthill McKee (because we can):\n     */\n\n    DoFRenumbering::Cuthill_McKee(dof_handler);\n\n    /*\n     * Reorder all (individual) export indices at the beginning of the\n     * locally_internal index range to achieve a better packing:\n     *\n     * Note: This function might miss export indices that come from\n     * eliminating hanging node and periodicity constraints (which we do\n     * not know at this point because they depend on the renumbering...).\n     */\n    DoFRenumbering::export_indices_first(dof_handler,\n                                         mpi_ensemble_.ensemble_communicator(),\n                                         n_locally_owned_,\n                                         1);\n\n    /*\n     * Group degrees of freedom that have the same stencil size in groups\n     * of multiples of the VectorizedArray<Number>::size().\n     *\n     * In order to determine the stencil size we have to create a first,\n     * temporary sparsity pattern:\n     */\n    create_constraints_and_sparsity_pattern();\n    n_locally_internal_ = DoFRenumbering::internal_range(\n        dof_handler, sparsity_pattern_, VectorizedArray<Number>::size());\n\n    /*\n     * Reorder all (strides of) locally internal indices that contain\n     * export indices to the start of the index range. This reordering\n     * preserves the binning introduced by\n     * DoFRenumbering::internal_range().\n     *\n     * Note: This function might miss export indices that come from\n     * eliminating hanging node and periodicity constraints (which we do\n     * not know at this point because they depend on the renumbering...).\n     * We therefore have to update n_export_indices_ later again.\n     */\n    n_export_indices_ = DoFRenumbering::export_indices_first(\n        dof_handler,\n        mpi_ensemble_.ensemble_communicator(),\n        n_locally_internal_,\n        VectorizedArray<Number>::size());\n  }\n\n\n  /*\n   * Modifies:\n   *     n_locally_internal_\n   */\n  template <int dim, typename Number>\n  void OfflineData<dim, Number>::ensure_simd_stride_consistency()\n  {\n    auto &dof_handler = this->dof_handler();\n\n    /*\n     * A small lambda to check for stride-level consistency of the internal\n     * index range:\n     */\n    const auto consistent_stride_range [[maybe_unused]] = [&]() {\n      constexpr auto group_size = VectorizedArray<Number>::size();\n      const IndexSet &locally_owned = dof_handler.locally_owned_dofs();\n      const auto offset = n_locally_owned_ != 0 ? *locally_owned.begin() : 0;\n\n      unsigned int group_row_length = 0;\n      unsigned int i = 0;\n      for (; i < n_locally_internal_; ++i) {\n        if (i % group_size == 0) {\n          group_row_length = sparsity_pattern_.row_length(offset + i);\n        } else {\n          if (group_row_length != sparsity_pattern_.row_length(offset + i)) {\n            break;\n          }\n        }\n      }\n      return i / group_size * group_size;\n    };\n\n    /*\n     * A small lambda that performs a \"logical or\" over all MPI ranks:\n     */\n    const auto mpi_allreduce_logical_or = [&](const bool local_value) {\n      std::function<bool(const bool &, const bool &)> comparator =\n          [](const bool &left, const bool &right) -> bool {\n        return left || right;\n      };\n      return Utilities::MPI::all_reduce(\n          local_value, mpi_ensemble_.ensemble_communicator(), comparator);\n    };\n\n    /*\n     * We have to ensure that the locally internal numbering range is still\n     * consistent, meaning that all strides have the same stencil size.\n     * This property might not hold any more after the elimination\n     * procedure of constrained degrees of freedom (periodicity, or hanging\n     * node constraints). Therefore, the following little dance:\n     */\n\n    const auto &affine_constraints = this->affine_constraints();\n    if (mpi_allreduce_logical_or(affine_constraints.n_constraints() > 0)) {\n      if (mpi_allreduce_logical_or( //\n              consistent_stride_range() != n_locally_internal_)) {\n        /*\n         * In this case we try to fix up the numbering by pushing affected\n         * strides to the end and slightly lowering the n_locally_internal_\n         * marker.\n         */\n        n_locally_internal_ = DoFRenumbering::inconsistent_strides_last(\n            dof_handler,\n            sparsity_pattern_,\n            n_locally_internal_,\n            VectorizedArray<Number>::size());\n        create_constraints_and_sparsity_pattern();\n        n_locally_internal_ = consistent_stride_range();\n      }\n    }\n\n    /*\n     * Check that after all the dof manipulation and setup we still end up\n     * with indices in [0, locally_internal) that have uniform stencil size\n     * within a stride.\n     */\n    Assert(consistent_stride_range() == n_locally_internal_,\n           dealii::ExcInternalError());\n  }\n\n\n  /*\n   * Populates:\n   *     affine_constraints_cg_\n   *     affine_constraints_dg_\n   *     n_locally_owned_\n   *     sparsity_pattern_\n   */\n  template <int dim, typename Number>\n  void OfflineData<dim, Number>::create_constraints_and_sparsity_pattern()\n  {\n    /*\n     * First, we set up the (globally indexed) affine constraints object\n     * for the continuous dof handler. The affine constraints object solely\n     * stores (a) hanging node constraints, and (b) periodicity constraints.\n     */\n\n    const auto populate_affine_constraints = //\n        [&](const auto &dof_handler, auto &affine_constraints) {\n#if DEAL_II_VERSION_GTE(9, 6, 0)\n          const auto locally_relevant =\n              DoFTools::extract_locally_relevant_dofs(dof_handler);\n#else\n          IndexSet locally_relevant;\n          DoFTools::extract_locally_relevant_dofs(dof_handler,\n                                                  locally_relevant);\n#endif\n\n#if DEAL_II_VERSION_GTE(9, 6, 0)\n          const IndexSet &locally_owned = dof_handler.locally_owned_dofs();\n          affine_constraints.reinit(locally_owned, locally_relevant);\n#else\n          affine_constraints.reinit(locally_relevant);\n#endif\n          DoFTools::make_hanging_node_constraints(dof_handler,\n                                                  affine_constraints);\n\n          /*\n           * Enforce periodic boundary conditions. We assume that the mesh is in\n           * \"normal configuration.\"\n           */\n\n          const auto &periodic_faces =\n              discretization_->triangulation().get_periodic_face_map();\n\n          for (const auto &[left, value] : periodic_faces) {\n            const auto &[right, orientation] = value;\n\n            typename DoFHandler<dim>::cell_iterator dof_cell_left(\n                &left.first->get_triangulation(),\n                left.first->level(),\n                left.first->index(),\n                &dof_handler);\n\n            typename DoFHandler<dim>::cell_iterator dof_cell_right(\n                &right.first->get_triangulation(),\n                right.first->level(),\n                right.first->index(),\n                &dof_handler);\n\n            if constexpr (std::is_same_v<Number, double>) {\n              DoFTools::make_periodicity_constraints(\n                  dof_cell_left->face(left.second),\n                  dof_cell_right->face(right.second),\n                  affine_constraints,\n                  ComponentMask(),\n#if DEAL_II_VERSION_GTE(9, 6, 0)\n                  orientation);\n#else\n                  /* orientation */ orientation[0],\n                  /* flip */ orientation[1],\n                  /* rotation */ orientation[2]);\n#endif\n            } else {\n              AssertThrow(false, dealii::ExcNotImplemented());\n              __builtin_trap();\n            }\n          }\n\n          affine_constraints.close();\n\n#ifdef DEBUG\n          {\n            /* Check that constraints are consistent in parallel: */\n            const std::vector<IndexSet> &locally_owned_dofs =\n                Utilities::MPI::all_gather(\n                    mpi_ensemble_.ensemble_communicator(),\n                    dof_handler.locally_owned_dofs());\n            const IndexSet locally_active =\n                dealii::DoFTools::extract_locally_active_dofs(dof_handler);\n            Assert(affine_constraints.is_consistent_in_parallel(\n                       locally_owned_dofs,\n                       locally_active,\n                       mpi_ensemble_.ensemble_communicator(),\n                       /*verbose*/ true),\n                   ExcInternalError());\n          }\n#endif\n        };\n\n    populate_affine_constraints(*dof_handler_cg_, affine_constraints_cg_);\n    /* Note: for dG the affine constraints object will be empty. */\n    populate_affine_constraints(*dof_handler_dg_, affine_constraints_dg_);\n\n    /*\n     * Next, set up the (hyperbolic) sparsity pattern:\n     */\n\n    const auto &dof_handler = this->dof_handler();\n    const auto &affine_constraints = this->affine_constraints();\n    const IndexSet &locally_owned = dof_handler.locally_owned_dofs();\n    Assert(n_locally_owned_ == locally_owned.n_elements(),\n           dealii::ExcInternalError());\n\n#if DEAL_II_VERSION_GTE(9, 6, 0)\n    const auto locally_relevant =\n        DoFTools::extract_locally_relevant_dofs(dof_handler);\n#else\n    IndexSet locally_relevant;\n    DoFTools::extract_locally_relevant_dofs(dof_handler, locally_relevant);\n#endif\n\n    sparsity_pattern_.reinit(\n        dof_handler.n_dofs(), dof_handler.n_dofs(), locally_relevant);\n\n    if (discretization_->have_discontinuous_ansatz()) {\n      /*\n       * Create dG sparsity pattern:\n       */\n      DoFTools::make_extended_sparsity_pattern_dg(\n          dof_handler, sparsity_pattern_, affine_constraints, false);\n    } else {\n      /*\n       * Create cG sparsity pattern:\n       */\n      DoFTools::make_sparsity_pattern(\n          dof_handler, sparsity_pattern_, affine_constraints, false);\n    }\n\n    /*\n     * We have to complete the local stencil to have consistent size over\n     * all MPI ranks. Otherwise, MPI synchronization in our\n     * SparseMatrix class will fail.\n     */\n\n    SparsityTools::distribute_sparsity_pattern(\n        sparsity_pattern_,\n        locally_owned,\n        mpi_ensemble_.ensemble_communicator(),\n        locally_relevant);\n  }\n\n\n  /*\n   * Populates:\n   *     n_locally_relevant_\n   *     scalar_partitioner_\n   *     hyperbolic_vector_partitioner_\n   *     precomputed_vector_partitioner_\n   *     sparsity_pattern_simd_\n   */\n  template <int dim, typename Number>\n  void OfflineData<dim, Number>::create_partitioner_and_simd_sparsity(\n      const unsigned int problem_dimension,\n      const unsigned int n_precomputed_values)\n  {\n    const auto &dof_handler = this->dof_handler();\n    const auto &affine_constraints = this->affine_constraints();\n    const IndexSet &locally_owned = dof_handler.locally_owned_dofs();\n    Assert(n_locally_owned_ == locally_owned.n_elements(),\n           dealii::ExcInternalError());\n\n#if DEAL_II_VERSION_GTE(9, 6, 0)\n    auto locally_relevant =\n        DoFTools::extract_locally_relevant_dofs(dof_handler);\n#else\n    IndexSet locally_relevant;\n    DoFTools::extract_locally_relevant_dofs(dof_handler, locally_relevant);\n#endif\n    /* Enlarge the locally relevant set to include all additional couplings: */\n    {\n      IndexSet additional_dofs(dof_handler.n_dofs());\n      for (auto &entry : sparsity_pattern_)\n        if (!locally_relevant.is_element(entry.column())) {\n          Assert(locally_owned.is_element(entry.row()), ExcInternalError());\n          additional_dofs.add_index(entry.column());\n        }\n      additional_dofs.compress();\n      locally_relevant.add_indices(additional_dofs);\n      locally_relevant.compress();\n    }\n\n    n_locally_relevant_ = locally_relevant.n_elements();\n\n    scalar_partitioner_ = std::make_shared<dealii::Utilities::MPI::Partitioner>(\n        locally_owned, locally_relevant, mpi_ensemble_.ensemble_communicator());\n\n    hyperbolic_vector_partitioner_ = Vectors::create_vector_partitioner(\n        scalar_partitioner_, problem_dimension);\n\n    precomputed_vector_partitioner_ = Vectors::create_vector_partitioner(\n        scalar_partitioner_, n_precomputed_values);\n\n    /*\n     * A small lambda that performs a \"logical or\" over all MPI ranks:\n     */\n    const auto mpi_allreduce_logical_or = [&](const bool local_value) {\n      std::function<bool(const bool &, const bool &)> comparator =\n          [](const bool &left, const bool &right) -> bool {\n        return left || right;\n      };\n      return Utilities::MPI::all_reduce(\n          local_value, mpi_ensemble_.ensemble_communicator(), comparator);\n    };\n\n    /*\n     * After eliminiating periodicity and hanging node constraints we need\n     * to update n_export_indices_ again. This happens because we need to\n     * call export_indices_first() with incomplete information (missing\n     * eliminated degrees of freedom).\n     */\n    if (mpi_allreduce_logical_or(affine_constraints.n_constraints() > 0)) {\n      /*\n       * Recalculate n_export_indices_:\n       */\n      n_export_indices_ = 0;\n      for (const auto &it : scalar_partitioner_->import_indices())\n        if (it.second <= n_locally_internal_)\n          n_export_indices_ = std::max(n_export_indices_, it.second);\n\n      constexpr auto simd_length = VectorizedArray<Number>::size();\n      n_export_indices_ =\n          (n_export_indices_ + simd_length - 1) / simd_length * simd_length;\n    }\n\n#ifdef DEBUG\n    /* Check that n_export_indices_ is valid: */\n    unsigned int control = 0;\n    for (const auto &it : scalar_partitioner_->import_indices())\n      if (it.second <= n_locally_internal_)\n        control = std::max(control, it.second);\n\n    Assert(control <= n_export_indices_, ExcInternalError());\n    Assert(n_export_indices_ <= n_locally_internal_, ExcInternalError());\n#endif\n\n    /*\n     * Set up SIMD sparsity pattern in local numbering.\n     *\n     * Nota bene: The SparsityPattern::reinit() function translates the\n     * pattern from global deal.II (typical) dof indexing to local indices.\n     */\n\n    sparsity_pattern_simd_.reinit(\n        n_locally_internal_, sparsity_pattern_, scalar_partitioner_);\n  }\n\n\n  template <int dim, typename Number>\n  void OfflineData<dim, Number>::create_matrices()\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"OfflineData<dim, Number>::create_matrices()\" << std::endl;\n#endif\n\n    /*\n     * First, (re)initialize all local matrices:\n     */\n\n    mass_matrix_.reinit(sparsity_pattern_simd_);\n    if (discretization_->have_discontinuous_ansatz())\n      mass_matrix_inverse_.reinit(sparsity_pattern_simd_);\n\n    lumped_mass_matrix_.reinit_with_scalar_partitioner(scalar_partitioner_);\n    lumped_mass_matrix_inverse_.reinit_with_scalar_partitioner(\n        scalar_partitioner_);\n\n    betaij_matrix_.reinit(sparsity_pattern_simd_);\n    cij_matrix_.reinit(sparsity_pattern_simd_);\n    if (discretization_->have_discontinuous_ansatz())\n      incidence_matrix_.reinit(sparsity_pattern_simd_);\n\n    /*\n     * Now, assemble all matrices:\n     */\n\n    const auto &dof_handler = this->dof_handler();\n    const auto &affine_constraints = this->affine_constraints();\n\n    measure_of_omega_ = 0.;\n\n    /* The local, per-cell assembly routine: */\n    const auto local_assemble_system = [&](const auto &cell,\n                                           auto &scratch,\n                                           auto &copy) {\n      /* iterate over locally owned cells and the ghost layer */\n\n      auto &is_locally_owned = copy.is_locally_owned_;\n      auto &local_dof_indices = copy.local_dof_indices_;\n      auto &neighbor_local_dof_indices = copy.neighbor_local_dof_indices_;\n\n      auto &cell_mass_matrix = copy.cell_mass_matrix_;\n      auto &cell_mass_matrix_inverse = copy.cell_mass_matrix_inverse_;\n      auto &cell_betaij_matrix = copy.cell_betaij_matrix_;\n      auto &cell_cij_matrix = copy.cell_cij_matrix_;\n      auto &interface_cij_matrix = copy.interface_cij_matrix_;\n      auto &cell_measure = copy.cell_measure_;\n\n      auto &hp_fe_values = scratch.hp_fe_values_;\n      auto &hp_fe_face_values = scratch.hp_fe_face_values_;\n      auto &hp_fe_neighbor_face_values = scratch.hp_fe_neighbor_face_values_;\n\n      is_locally_owned = cell->is_locally_owned(); /* stored in copy object */\n      if (!is_locally_owned)\n        return;\n\n      const unsigned int dofs_per_cell = cell->get_fe().n_dofs_per_cell();\n\n      cell_mass_matrix.reinit(dofs_per_cell, dofs_per_cell);\n      cell_betaij_matrix.reinit(dofs_per_cell, dofs_per_cell);\n      for (auto &matrix : cell_cij_matrix)\n        matrix.reinit(dofs_per_cell, dofs_per_cell);\n      if (discretization_->have_discontinuous_ansatz()) {\n        cell_mass_matrix_inverse.reinit(dofs_per_cell, dofs_per_cell);\n      }\n\n      hp_fe_values.reinit(cell);\n      const auto &fe_values = hp_fe_values.get_present_fe_values();\n\n      local_dof_indices.resize(dofs_per_cell);\n      cell->get_dof_indices(local_dof_indices);\n\n      /* clear out copy data: */\n\n      cell_mass_matrix = 0.;\n      cell_betaij_matrix = 0.;\n      for (auto &matrix : cell_cij_matrix)\n        matrix = 0.;\n      if (discretization_->have_discontinuous_ansatz()) {\n        cell_mass_matrix_inverse = 0.;\n      }\n      cell_measure = 0.;\n\n      for (unsigned int q : fe_values.quadrature_point_indices()) {\n        const auto JxW = fe_values.JxW(q);\n\n        /* skip cells that have no active cells when computing domain size: */\n        if (dofs_per_cell != 0 && cell->is_locally_owned())\n          cell_measure += Number(JxW);\n\n        for (unsigned int j : fe_values.dof_indices()) {\n          const auto value_JxW = fe_values.shape_value(j, q) * JxW;\n          const auto grad_JxW = fe_values.shape_grad(j, q) * JxW;\n\n          for (unsigned int i : fe_values.dof_indices()) {\n            const auto value = fe_values.shape_value(i, q);\n            const auto grad = fe_values.shape_grad(i, q);\n\n            cell_mass_matrix(i, j) += Number(value * value_JxW);\n            cell_betaij_matrix(i, j) += Number(grad * grad_JxW);\n            for (unsigned int d = 0; d < dim; ++d)\n              cell_cij_matrix[d](i, j) += Number((value * grad_JxW)[d]);\n          } /* for i */\n        }   /* for j */\n      }     /* for q */\n\n      /*\n       * For a discontinuous finite element ansatz we need to assemble\n       * additional face contributions:\n       */\n\n      if (!discretization_->have_discontinuous_ansatz())\n        return;\n\n      for (const auto f_index : cell->face_indices()) {\n        const auto &face = cell->face(f_index);\n\n        /* Skip faces without neighbors... */\n        const bool has_neighbor =\n            !face->at_boundary() || cell->has_periodic_neighbor(f_index);\n        if (!has_neighbor) {\n          // set the vector of local dof indices to 0 to indicate that\n          // there is nothing to do for this face:\n          neighbor_local_dof_indices[f_index].resize(0);\n          continue;\n        }\n\n        /* Avoid artificial cells: */\n        const auto neighbor_cell = cell->neighbor_or_periodic_neighbor(f_index);\n        if (neighbor_cell->is_artificial()) {\n          // set the vector of local dof indices to 0 to indicate that\n          // there is nothing to do for this face:\n          neighbor_local_dof_indices[f_index].resize(0);\n          continue;\n        }\n\n        /* Do not assemble jumps to neighboring cells with FE_Nothing: */\n        const bool neighbor_cell_has_fe_nothing =\n            treat_fe_nothing_as_boundary_ &&\n            (dynamic_cast<const dealii::FE_Nothing<dim> *>(\n                 &neighbor_cell->get_fe()) != nullptr);\n        if (neighbor_cell_has_fe_nothing) {\n          // set the vector of local dof indices to 0 to indicate that\n          // there is nothing to do for this face:\n          neighbor_local_dof_indices[f_index].resize(0);\n          continue;\n        }\n\n        hp_fe_face_values.reinit(cell, f_index);\n        const auto &fe_face_values = hp_fe_face_values.get_present_fe_values();\n\n        /* Face contribution: */\n\n        for (unsigned int q : fe_face_values.quadrature_point_indices()) {\n          const auto JxW = fe_face_values.JxW(q);\n          const auto &normal = fe_face_values.get_normal_vectors()[q];\n\n          for (unsigned int j : fe_face_values.dof_indices()) {\n            const auto value_JxW = fe_face_values.shape_value(j, q) * JxW;\n\n            for (unsigned int i : fe_face_values.dof_indices()) {\n              const auto value = fe_face_values.shape_value(i, q);\n\n              for (unsigned int d = 0; d < dim; ++d)\n                cell_cij_matrix[d](i, j) -=\n                    Number(0.5 * normal[d] * value * value_JxW);\n            } /* for i */\n          }   /* for j */\n        }     /* for q */\n\n        /* Coupling part: */\n\n        const unsigned int f_index_neighbor =\n            cell->has_periodic_neighbor(f_index)\n                ? cell->periodic_neighbor_of_periodic_neighbor(f_index)\n                : cell->neighbor_of_neighbor(f_index);\n\n        const unsigned int neighbor_dofs_per_cell =\n            neighbor_cell->get_fe().n_dofs_per_cell();\n        neighbor_local_dof_indices[f_index].resize(neighbor_dofs_per_cell);\n        neighbor_cell->get_dof_indices(neighbor_local_dof_indices[f_index]);\n\n        for (unsigned int k = 0; k < dim; ++k) {\n          interface_cij_matrix[f_index][k].reinit(dofs_per_cell,\n                                                  neighbor_dofs_per_cell);\n          interface_cij_matrix[f_index][k] = 0.;\n        }\n\n        hp_fe_neighbor_face_values.reinit(neighbor_cell, f_index_neighbor);\n        const auto &fe_neighbor_face_values =\n            hp_fe_neighbor_face_values.get_present_fe_values();\n\n        for (unsigned int q : fe_face_values.quadrature_point_indices()) {\n          const auto JxW = fe_face_values.JxW(q);\n          const auto &normal = fe_face_values.get_normal_vectors()[q];\n\n          /* index j for neighbor, index i for current cell: */\n          for (unsigned int j : fe_neighbor_face_values.dof_indices()) {\n            const auto value_JxW =\n                fe_neighbor_face_values.shape_value(j, q) * JxW;\n\n            for (unsigned int i : fe_face_values.dof_indices()) {\n              const auto value = fe_face_values.shape_value(i, q);\n\n              for (unsigned int d = 0; d < dim; ++d)\n                interface_cij_matrix[f_index][d](i, j) +=\n                    Number(0.5 * normal[d] * value * value_JxW);\n            } /* for i */\n          }   /* for j */\n        }     /* for q */\n      }\n\n      /*\n       * Compute block inverse of mass matrix:\n       */\n\n      if (discretization_->have_discontinuous_ansatz()) {\n        // FIXME: rewrite with CellwiseInverseMassMatrix\n        if (!cell_mass_matrix_inverse.empty())\n          cell_mass_matrix_inverse.invert(cell_mass_matrix);\n      }\n    };\n\n    const auto copy_local_to_global = [&](const auto &copy) {\n      const auto &is_locally_owned = copy.is_locally_owned_;\n      const auto &dof_indices = copy.local_dof_indices_;\n      const auto &neighbor_dof_indices = copy.neighbor_local_dof_indices_;\n      const auto &cell_mass_matrix = copy.cell_mass_matrix_;\n      const auto &cell_mass_matrix_inverse = copy.cell_mass_matrix_inverse_;\n      const auto &cell_cij_matrix = copy.cell_cij_matrix_;\n      const auto &interface_cij_matrix = copy.interface_cij_matrix_;\n      const auto &cell_betaij_matrix = copy.cell_betaij_matrix_;\n      const auto &cell_measure = copy.cell_measure_;\n\n      if (!is_locally_owned)\n        return;\n\n      distribute_local_to_global(\n          cell_mass_matrix, dof_indices, affine_constraints, mass_matrix_);\n\n      distribute_local_to_global(\n          cell_cij_matrix, dof_indices, affine_constraints, cij_matrix_);\n\n      /*\n       * Workaround: We need to catch the case local_dof_indices.size() == 0\n       * because deal.II reports the wrong size in the matrix object.\n       */\n      if (dof_indices.size() != 0) {\n        for (unsigned int f_index = 0; f_index < copy.n_faces; ++f_index) {\n          if (neighbor_dof_indices[f_index].size() != 0) {\n            distribute_local_to_global(interface_cij_matrix[f_index],\n                                       dof_indices,\n                                       neighbor_dof_indices[f_index],\n                                       affine_constraints,\n                                       cij_matrix_);\n          }\n        }\n      }\n\n      distribute_local_to_global(\n          cell_betaij_matrix, dof_indices, affine_constraints, betaij_matrix_);\n\n      if (discretization_->have_discontinuous_ansatz())\n        distribute_local_to_global(cell_mass_matrix_inverse,\n                                   dof_indices,\n                                   affine_constraints,\n                                   mass_matrix_inverse_);\n\n      measure_of_omega_ += cell_measure;\n    };\n\n    WorkStream::run(dof_handler.begin_active(),\n                    dof_handler.end(),\n                    local_assemble_system,\n                    copy_local_to_global,\n                    AssemblyScratchData<dim>(*discretization_),\n                    AssemblyCopyData<dim, Number>());\n\n    mass_matrix_.compress(VectorOperation::add);\n    mass_matrix_.update_ghost_rows();\n    cij_matrix_.compress(VectorOperation::add);\n    cij_matrix_.update_ghost_rows();\n    betaij_matrix_.compress(VectorOperation::add);\n    betaij_matrix_.update_ghost_rows();\n    if (discretization_->have_discontinuous_ansatz()) {\n      mass_matrix_inverse_.compress(VectorOperation::add);\n      mass_matrix_inverse_.update_ghost_rows();\n    }\n\n    measure_of_omega_ = Utilities::MPI::sum(\n        measure_of_omega_, mpi_ensemble_.ensemble_communicator());\n\n    /*\n     * Create lumped mass matrix:\n     */\n\n    {\n      const auto body = [&](auto sentinel, unsigned int i) {\n        using T = decltype(sentinel);\n        constexpr unsigned int stride_size = get_stride_size<T>;\n\n        /* Skip constrained degrees of freedom: */\n        const unsigned int row_length = sparsity_pattern_simd_.row_length(i);\n        if (row_length == 1)\n          return;\n\n        T m_i{};\n\n        const unsigned int *js = sparsity_pattern_simd_.columns(i);\n        for (unsigned int col_idx = 0; col_idx < row_length;\n             ++col_idx, js += stride_size) {\n\n          const auto m_ij = mass_matrix_.template read_entry<T>(i, col_idx);\n          m_i += m_ij;\n        }\n\n        lumped_mass_matrix_.template write_entry<T>(m_i, i);\n        lumped_mass_matrix_inverse_. //\n            template write_entry<T>(Number(1.) / m_i, i);\n      };\n\n      cpu_simd_loop<Number>(\"\", body, 0, n_locally_internal_, n_locally_owned_);\n\n      lumped_mass_matrix_.update_ghost_values();\n      lumped_mass_matrix_inverse_.update_ghost_values();\n    }\n\n    /*\n     * Assemble incidence matrix:\n     */\n\n    if (discretization_->have_discontinuous_ansatz()) {\n      /* The local, per-cell assembly routine: */\n      const auto local_assemble_system = [&](const auto &cell,\n                                             auto &scratch,\n                                             auto &copy) {\n        /* iterate over locally owned cells and the ghost layer */\n\n        auto &is_locally_owned = copy.is_locally_owned_;\n        auto &local_dof_indices = copy.local_dof_indices_;\n        auto &neighbor_local_dof_indices = copy.neighbor_local_dof_indices_;\n        auto &interface_incidence_matrix = copy.interface_incidence_matrix_;\n        auto &hp_fe_face_values_nodal = scratch.hp_fe_face_values_nodal_;\n        auto &hp_fe_neighbor_face_values_nodal =\n            scratch.hp_fe_neighbor_face_values_nodal_;\n\n        is_locally_owned = cell->is_locally_owned(); /* stored in copy object */\n        if (!is_locally_owned)\n          return;\n\n        const unsigned int dofs_per_cell = cell->get_fe().n_dofs_per_cell();\n\n        for (auto &matrix : interface_incidence_matrix)\n          matrix.reinit(dofs_per_cell, dofs_per_cell);\n\n        local_dof_indices.resize(dofs_per_cell);\n        cell->get_dof_indices(local_dof_indices);\n\n        /* clear out copy data: */\n        for (auto &matrix : interface_incidence_matrix)\n          matrix = 0.;\n\n        for (const auto f_index : cell->face_indices()) {\n          const auto &face = cell->face(f_index);\n\n          /* Skip faces without neighbors... */\n          const bool has_neighbor =\n              !face->at_boundary() || cell->has_periodic_neighbor(f_index);\n          if (!has_neighbor) {\n            // set the vector of local dof indices to 0 to indicate that\n            // there is nothing to do for this face:\n            neighbor_local_dof_indices[f_index].resize(0);\n            continue;\n          }\n\n          /* Avoid artificial cells: */\n          const auto neighbor_cell =\n              cell->neighbor_or_periodic_neighbor(f_index);\n          if (neighbor_cell->is_artificial()) {\n            // set the vector of local dof indices to 0 to indicate that\n            // there is nothing to do for this face:\n            neighbor_local_dof_indices[f_index].resize(0);\n            continue;\n          }\n\n          /* Do not assemble jumps to neighboring cells with FE_Nothing: */\n          const bool neighbor_cell_has_fe_nothing =\n              treat_fe_nothing_as_boundary_ &&\n              (dynamic_cast<const dealii::FE_Nothing<dim> *>(\n                   &neighbor_cell->get_fe()) != nullptr);\n          if (neighbor_cell_has_fe_nothing) {\n            neighbor_local_dof_indices[f_index].resize(0);\n            continue;\n          }\n\n          const unsigned int neighbor_dofs_per_cell =\n              neighbor_cell->get_fe().n_dofs_per_cell();\n          neighbor_local_dof_indices[f_index].resize(neighbor_dofs_per_cell);\n          neighbor_cell->get_dof_indices(neighbor_local_dof_indices[f_index]);\n\n          const unsigned int f_index_neighbor =\n              cell->has_periodic_neighbor(f_index)\n                  ? cell->periodic_neighbor_of_periodic_neighbor(f_index)\n                  : cell->neighbor_of_neighbor(f_index);\n\n          hp_fe_face_values_nodal.reinit(cell, f_index);\n          const auto &fe_face_values_nodal =\n              hp_fe_face_values_nodal.get_present_fe_values();\n          hp_fe_neighbor_face_values_nodal.reinit(neighbor_cell,\n                                                  f_index_neighbor);\n          const auto &fe_neighbor_face_values_nodal =\n              hp_fe_neighbor_face_values_nodal.get_present_fe_values();\n\n          /* Lumped incidence matrix: */\n\n          for (unsigned int q :\n               fe_face_values_nodal.quadrature_point_indices()) {\n            /* index j for neighbor, index i for current cell: */\n            for (unsigned int j : fe_neighbor_face_values_nodal.dof_indices()) {\n              const auto v_j = fe_neighbor_face_values_nodal.shape_value(j, q);\n              for (unsigned int i : fe_face_values_nodal.dof_indices()) {\n                const auto v_i = fe_face_values_nodal.shape_value(i, q);\n                constexpr auto eps = std::numeric_limits<Number>::epsilon();\n                if (std::abs(v_i * v_j) > 100. * eps) {\n                  const auto &ansatz = discretization_->ansatz();\n\n                  const auto global_i = local_dof_indices[i];\n                  const auto global_j = neighbor_local_dof_indices[f_index][j];\n                  const auto local_i =\n                      scalar_partitioner_->global_to_local(global_i);\n                  const auto local_j =\n                      scalar_partitioner_->global_to_local(global_j);\n                  const auto m_i = lumped_mass_matrix_.read_entry(local_i);\n                  const auto m_j = lumped_mass_matrix_.read_entry(local_j);\n                  const auto hd_ij =\n                      Number(0.5) * (m_i + m_j) / measure_of_omega_;\n\n                  Number r_ij = 1.0;\n\n                  if (ansatz == Ansatz::dg_q2) {\n                    /*\n                     * For even polynomial degree we normalize the incidence\n                     * matrix to (0.5 (m_i + m_j) / |Omega|) ^ (1.5 / d).\n                     * Note, that we will visit every coupling\n                     * pair of degrees of freedom (i, j) precisely once.\n                     */\n                    r_ij = std::pow(hd_ij, incidence_relaxation_even_ / dim);\n                  } else {\n                    /*\n                     * For odd polynomial degree we normalize the incidence\n                     * matrix to 1. Note, that we will visit every coupling\n                     * pair of degrees of freedom (i, j) precisely once.\n                     */\n                    r_ij = std::pow(hd_ij, incidence_relaxation_odd_ / dim);\n                  }\n\n                  interface_incidence_matrix[f_index](i, j) += r_ij;\n                }\n              } /* for i */\n            }   /* for j */\n          }     /* for q */\n        }\n      };\n\n      const auto copy_local_to_global = [&](const auto &copy) {\n        const auto &is_locally_owned = copy.is_locally_owned_;\n        const auto &dof_indices = copy.local_dof_indices_;\n        const auto &neighbor_dof_indices = copy.neighbor_local_dof_indices_;\n        const auto &interface_incidence_matrix =\n            copy.interface_incidence_matrix_;\n\n        if (!is_locally_owned)\n          return;\n\n        /*\n         * Workaround: We need to catch the case dof_indices.size() == 0\n         * because deal.II reports the wrong size in the matrix object.\n         */\n        if (dof_indices.size() != 0) {\n          for (unsigned int f_index = 0; f_index < copy.n_faces; ++f_index) {\n            if (neighbor_dof_indices[f_index].size() != 0) {\n              distribute_local_to_global(interface_incidence_matrix[f_index],\n                                         dof_indices,\n                                         neighbor_dof_indices[f_index],\n                                         affine_constraints,\n                                         incidence_matrix_);\n            }\n          }\n        }\n      };\n\n      WorkStream::run(dof_handler.begin_active(),\n                      dof_handler.end(),\n                      local_assemble_system,\n                      copy_local_to_global,\n                      AssemblyScratchData<dim>(*discretization_),\n                      AssemblyCopyData<dim, Number>());\n\n      incidence_matrix_.compress(VectorOperation::add);\n    }\n\n    /*\n     * Populate boundary map and collect coupling boundary pairs:\n     */\n    {\n      boundary_map_ = construct_boundary_map(\n          dof_handler.begin_active(), dof_handler.end(), *scalar_partitioner_);\n\n      coupling_boundary_pairs_ = collect_coupling_boundary_pairs(\n          dof_handler.begin_active(), dof_handler.end(), *scalar_partitioner_);\n    }\n\n#ifdef DEBUG_SYMMETRY_CHECK\n    /*\n     * Verify that we have consistent mass:\n     */\n\n    double total_mass = 0.;\n    for (unsigned int i = 0; i < n_locally_owned_; ++i)\n      total_mass += lumped_mass_matrix_.read_entry(i);\n    total_mass =\n        Utilities::MPI::sum(total_mass, mpi_ensemble_.ensemble_communicator());\n\n    Assert(std::abs(measure_of_omega_ - total_mass) <\n               1.e-12 * measure_of_omega_,\n           dealii::ExcMessage(\n               \"Total mass differs from the measure of the domain.\"));\n\n    /*\n     * Verify that the mij_matrix_ object is consistent:\n     */\n\n    for (unsigned int i = 0; i < n_locally_owned_; ++i) {\n      /* Skip constrained degrees of freedom: */\n      const unsigned int row_length = sparsity_pattern_simd_.row_length(i);\n      if (row_length == 1)\n        continue;\n\n      auto sum =\n          mass_matrix_.read_entry(i, 0) - lumped_mass_matrix_.read_entry(i);\n\n      /* skip diagonal */\n      constexpr auto simd_length = VectorizedArray<Number>::size();\n      const unsigned int *js = sparsity_pattern_simd_.columns(i);\n      for (unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {\n        const auto j = *(i < n_locally_internal_ ? js + col_idx * simd_length\n                                                 : js + col_idx);\n        Assert(j < n_locally_relevant_, dealii::ExcInternalError());\n\n        const auto m_ij = mass_matrix_.read_entry(i, col_idx);\n        if (discretization_->have_discontinuous_ansatz()) {\n          // Interfacial coupling terms are present in the stencil but zero\n          // in the mass matrix\n          Assert(std::abs(m_ij) > -1.e-12, dealii::ExcInternalError());\n        } else {\n          Assert(std::abs(m_ij) > 1.e-12, dealii::ExcInternalError());\n        }\n        sum += m_ij;\n\n        const auto m_ji = mass_matrix_.read_transposed_entry(i, col_idx);\n        if (std::abs(m_ij - m_ji) >= 1.e-12) {\n          // The m_ij matrix is not symmetric\n          std::stringstream ss;\n          ss << \"m_ij matrix is not symmetric: \" << m_ij << \" <-> \" << m_ji;\n          Assert(false, dealii::ExcMessage(ss.str()));\n        }\n      }\n\n      Assert(std::abs(sum) < 1.e-12, dealii::ExcInternalError());\n    }\n\n    /*\n     * Verify that the cij_matrix_ object is consistent:\n     */\n\n    for (unsigned int i = 0; i < n_locally_owned_; ++i) {\n      /* Skip constrained degrees of freedom: */\n      const unsigned int row_length = sparsity_pattern_simd_.row_length(i);\n      if (row_length == 1)\n        continue;\n\n      auto sum = cij_matrix_.read_tensor(i, 0);\n\n      /* skip diagonal */\n      constexpr auto simd_length = VectorizedArray<Number>::size();\n      const unsigned int *js = sparsity_pattern_simd_.columns(i);\n      for (unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {\n        const auto j = *(i < n_locally_internal_ ? js + col_idx * simd_length\n                                                 : js + col_idx);\n        Assert(j < n_locally_relevant_, dealii::ExcInternalError());\n\n        const auto c_ij = cij_matrix_.read_tensor(i, col_idx);\n        Assert(c_ij.norm() > 1.e-12, dealii::ExcInternalError());\n        sum += c_ij;\n\n        const auto c_ji = cij_matrix_.read_transposed_tensor(i, col_idx);\n        if ((c_ij + c_ji).norm() >= 1.e-12) {\n          // The c_ij matrix is not symmetric, this can only happen if i\n          // and j are both located on the boundary.\n\n          CouplingDescription coupling{i, col_idx, j};\n          const auto it = std::find(coupling_boundary_pairs_.begin(),\n                                    coupling_boundary_pairs_.end(),\n                                    coupling);\n          if (it == coupling_boundary_pairs_.end()) {\n            std::stringstream ss;\n            ss << \"c_ij matrix is not anti-symmetric: \" << c_ij << \" <-> \"\n               << c_ji;\n            Assert(false, dealii::ExcMessage(ss.str()));\n          }\n        }\n      }\n\n      Assert(sum.norm() < 1.e-12, dealii::ExcInternalError());\n    }\n#endif\n  }\n\n\n  template <int dim, typename Number>\n  void OfflineData<dim, Number>::create_multigrid_data()\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"OfflineData<dim, Number>::create_multigrid_data()\"\n              << std::endl;\n#endif\n\n    Assert(!dof_handler_cg_->has_hp_capabilities(), dealii::ExcInternalError());\n    Assert(!dof_handler_dg_->has_hp_capabilities(), dealii::ExcInternalError());\n\n    dof_handler_cg_->distribute_mg_dofs();\n    dof_handler_dg_->distribute_mg_dofs();\n\n    /* Now, work on data structures for hyperbolic update: */\n\n    auto &dof_handler = this->dof_handler();\n\n    const auto n_levels = dof_handler.get_triangulation().n_global_levels();\n\n    AffineConstraints<float> level_constraints;\n    // TODO not yet thread-parallel and without periodicity\n\n    level_boundary_map_.resize(n_levels);\n    level_lumped_mass_matrix_.resize(n_levels);\n\n    for (unsigned int level = 0; level < n_levels; ++level) {\n      /* Assemble lumped mass matrix vector: */\n\n#if DEAL_II_VERSION_GTE(9, 6, 0)\n      const auto relevant_dofs =\n          dealii::DoFTools::extract_locally_relevant_level_dofs(dof_handler,\n                                                                level);\n#else\n      IndexSet relevant_dofs;\n      dealii::DoFTools::extract_locally_relevant_level_dofs(\n          dof_handler, level, relevant_dofs);\n#endif\n\n      const auto partitioner = std::make_shared<Utilities::MPI::Partitioner>(\n          dof_handler.locally_owned_mg_dofs(level),\n          relevant_dofs,\n          mpi_ensemble_.ensemble_communicator());\n      level_lumped_mass_matrix_[level].reinit(partitioner);\n      std::vector<types::global_dof_index> dof_indices(\n          dof_handler.get_fe().dofs_per_cell);\n      dealii::Vector<Number> mass_values(dof_handler.get_fe().dofs_per_cell);\n      dealii::hp::FEValues<dim> hp_fe_values(discretization_->mapping(),\n                                             discretization_->finite_element(),\n                                             discretization_->quadrature(),\n                                             update_values | update_JxW_values);\n      for (const auto &cell : dof_handler.cell_iterators_on_level(level))\n        // TODO for assembly with dealii::SparseMatrix and local\n        // numbering this probably has to read !cell->is_artificial()\n        if (cell->is_locally_owned_on_level()) {\n          hp_fe_values.reinit(cell);\n          const auto &fe_values = hp_fe_values.get_present_fe_values();\n          for (unsigned int i = 0; i < mass_values.size(); ++i) {\n            double sum = 0;\n            for (unsigned int q = 0; q < fe_values.n_quadrature_points; ++q)\n              sum += fe_values.shape_value(i, q) * fe_values.JxW(q);\n            mass_values(i) = sum;\n          }\n          cell->get_mg_dof_indices(dof_indices);\n          level_constraints.distribute_local_to_global(\n              mass_values, dof_indices, level_lumped_mass_matrix_[level]);\n        }\n      level_lumped_mass_matrix_[level].compress(VectorOperation::add);\n\n      /* Populate boundary map: */\n\n      level_boundary_map_[level] = construct_boundary_map(\n          dof_handler.begin_mg(level), dof_handler.end_mg(level), *partitioner);\n    }\n  }\n\n\n  template <int dim, typename Number>\n  template <typename ITERATOR1, typename ITERATOR2>\n  auto OfflineData<dim, Number>::construct_boundary_map(\n      const ITERATOR1 &begin,\n      const ITERATOR2 &end,\n      const Utilities::MPI::Partitioner &partitioner) const -> BoundaryMap\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"OfflineData<dim, Number>::construct_boundary_map()\"\n              << std::endl;\n#endif\n\n    /*\n     * Create a temporary multimap with the (local) dof index as key:\n     */\n\n    using BoundaryData = std::tuple<dealii::Tensor<1, dim, Number> /*normal*/,\n                                    Number /*normal mass*/,\n                                    Number /*boundary mass*/,\n                                    dealii::types::boundary_id /*id*/,\n                                    dealii::Point<dim>> /*position*/;\n    std::multimap<unsigned int, BoundaryData> preliminary_map;\n\n    std::vector<dealii::types::global_dof_index> local_dof_indices;\n\n    dealii::hp::FEFaceValues<dim> hp_fe_face_values(\n        discretization_->mapping(),\n        discretization_->finite_element(),\n        discretization_->face_quadrature(),\n        dealii::update_normal_vectors | dealii::update_values |\n            dealii::update_JxW_values);\n\n    for (auto cell = begin; cell != end; ++cell) {\n\n      /*\n       * Workaround: Make sure to iterate over the entire locally relevant\n       * set so that we compute normals between cells with differing owners\n       * correctly. This strategy works for 2D but will fail in 3D with\n       * locally refined meshes and hanging nodes situated at the boundary.\n       */\n      if ((cell->is_active() && cell->is_artificial()) ||\n          (!cell->is_active() && cell->is_artificial_on_level()))\n        continue;\n\n      const unsigned int dofs_per_cell = cell->get_fe().n_dofs_per_cell();\n      const auto &support_points = cell->get_fe().get_unit_support_points();\n\n      local_dof_indices.resize(dofs_per_cell);\n      cell->get_active_or_mg_dof_indices(local_dof_indices);\n\n      for (auto f_index : cell->face_indices()) {\n        const auto face = cell->face(f_index);\n        auto id = face->boundary_id();\n\n        /*\n         * Skip periodic boundary faces. For our algorithm these are\n         * interior degrees of freedom (if not simultaneously located at\n         * another boundary as well).\n         */\n        if (id == Boundary::periodic)\n          continue;\n\n        /*\n         * Check for interior faces neighboring a cell with FE nothing:\n         */\n        const bool neighbor_cell_has_fe_nothing =                 //\n            treat_fe_nothing_as_boundary_ &&                      //\n            !face->at_boundary() &&                               //\n            cell->neighbor(f_index)->is_active() &&               //\n            !cell->neighbor(f_index)->is_artificial() &&          //\n            (dynamic_cast<const dealii::FE_Nothing<dim> *>(       //\n                 &cell->neighbor(f_index)->get_fe()) != nullptr); //\n\n        if (neighbor_cell_has_fe_nothing) {\n          /*\n           * By convention, query boundary id from the material id of the\n           * neighboring cell and then continue:\n           */\n          id = cell->neighbor(f_index)->material_id();\n\n        } else if (!face->at_boundary()) {\n          /* Bail out if we are in the interior: */\n          continue;\n        }\n\n        hp_fe_face_values.reinit(cell, f_index);\n        const auto &fe_face_values = hp_fe_face_values.get_present_fe_values();\n        const auto &mapping =\n            hp_fe_face_values.get_mapping_collection()[cell->active_fe_index()];\n\n        for (unsigned int j : fe_face_values.dof_indices()) {\n          if (!cell->get_fe().has_support_on_face(j, f_index))\n            continue;\n\n          Number boundary_mass = 0.;\n          dealii::Tensor<1, dim, Number> normal;\n\n          for (unsigned int q : fe_face_values.quadrature_point_indices()) {\n            const auto JxW = fe_face_values.JxW(q);\n            const auto phi_i = fe_face_values.shape_value(j, q);\n\n            boundary_mass += phi_i * JxW;\n            normal += phi_i * fe_face_values.normal_vector(q) * JxW;\n          }\n\n          /*\n           * Workaround for deal.II 9.7 and older versions:\n           *\n           * For simplices, has_support_on_face() seems to return the wrong\n           * answer (always true). Thus, check whether we accumulated any\n           * boundary mass and if not, bail out.\n           */\n          if (std::abs(boundary_mass) == 0.)\n            continue;\n\n          const auto global_index = local_dof_indices[j];\n          const auto index = partitioner.global_to_local(global_index);\n\n          /* Skip nonlocal degrees of freedom: */\n          if (index >= n_locally_owned_)\n            continue;\n\n          /* Skip constrained degrees of freedom: */\n          const unsigned int row_length =\n              sparsity_pattern_simd_.row_length(index);\n          if (row_length == 1)\n            continue;\n\n          Point<dim> position =\n              mapping.transform_unit_to_real_cell(cell, support_points[j]);\n\n          /*\n           * Temporarily insert a (wrong) boundary mass value for the\n           * normal mass. We'll fix this later.\n           */\n          preliminary_map.insert(\n              {index, {normal, boundary_mass, boundary_mass, id, position}});\n        } /* j */\n      }   /* f */\n    }     /* cell */\n\n    /*\n     * Filter boundary map:\n     *\n     * At this point we have collected multiple cell contributions for each\n     * boundary degree of freedom. We now merge all entries that have the\n     * same boundary id and whose normals describe an acute angle of about\n     * 60 degrees or less.\n     *\n     * FIXME: is this robust in 3D?\n     */\n\n    std::multimap<unsigned int, BoundaryData> filtered_map;\n    std::set<dealii::types::global_dof_index> boundary_dofs;\n    for (auto entry : preliminary_map) {\n      bool inserted = false;\n      const auto range = filtered_map.equal_range(entry.first);\n      for (auto it = range.first; it != range.second; ++it) {\n        auto &[new_normal,\n               new_normal_mass,\n               new_boundary_mass,\n               new_id,\n               new_point] = entry.second;\n        auto &[normal, normal_mass, boundary_mass, id, point] = it->second;\n\n        if (id != new_id)\n          continue;\n\n        Assert(point.distance(new_point) < 1.0e-14, dealii::ExcInternalError());\n\n        if (normal * new_normal / normal.norm() / new_normal.norm() > 0.50) {\n          /*\n           * Both normals describe an acute angle of 85 degrees or less.\n           * Merge the entries and continue.\n           */\n          normal += new_normal;\n          boundary_mass += new_boundary_mass;\n          inserted = true;\n          continue;\n\n        } else if constexpr (dim == 2) {\n          /*\n           * Workaround for 2D: When enforcing slip boundary conditions\n           * with two noncollinear vectors the resulting momentum must be\n           * 0. But the normals don't necessarily describe an orthonormal\n           * basis and we cannot use orthogonal projection. Therefore,\n           * simply set the boundary type to no slip:\n           */\n          if (new_id == Boundary::slip) {\n            Assert(id == Boundary::slip, dealii::ExcInternalError());\n            new_id = Boundary::no_slip;\n            id = Boundary::no_slip;\n          }\n        }\n      }\n      if (!inserted)\n        filtered_map.insert(entry);\n    }\n\n    /*\n     * Normalize all normal vectors and create final boundary_map:\n     */\n\n    BoundaryMap boundary_map;\n    std::transform(\n        std::begin(filtered_map),\n        std::end(filtered_map),\n        std::back_inserter(boundary_map),\n        [&](const auto &it) -> BoundaryDescription { //\n          auto index = it.first;\n          const auto &[normal, normal_mass, boundary_mass, id, point] =\n              it.second;\n\n          const auto new_normal_mass =\n              normal.norm() + std::numeric_limits<Number>::epsilon();\n          const auto new_normal = normal / new_normal_mass;\n\n          return {index, new_normal, new_normal_mass, boundary_mass, id, point};\n        });\n\n    return boundary_map;\n  }\n\n\n  template <int dim, typename Number>\n  template <typename ITERATOR1, typename ITERATOR2>\n  auto OfflineData<dim, Number>::collect_coupling_boundary_pairs(\n      const ITERATOR1 &begin,\n      const ITERATOR2 &end,\n      const Utilities::MPI::Partitioner &partitioner) const\n      -> CouplingBoundaryPairs\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"OfflineData<dim, Number>::collect_coupling_boundary_pairs()\"\n              << std::endl;\n#endif\n\n    /*\n     * First, collect *all* locally relevant degrees of freedom that are\n     * located on a (non periodic) boundary. We also collect constrained\n     * degrees of freedom for the time being (and filter afterwards).\n     */\n\n    std::set<unsigned int> locally_relevant_boundary_indices;\n\n    std::vector<dealii::types::global_dof_index> local_dof_indices;\n\n    for (auto cell = begin; cell != end; ++cell) {\n\n      /* Make sure to iterate over the entire locally relevant set: */\n      if (cell->is_artificial())\n        continue;\n\n      const auto &finite_element = cell->get_fe();\n      const unsigned int dofs_per_cell = finite_element.dofs_per_cell;\n      local_dof_indices.resize(dofs_per_cell);\n      cell->get_active_or_mg_dof_indices(local_dof_indices);\n\n      for (auto f_index : cell->face_indices()) {\n        const auto face = cell->face(f_index);\n        const auto id = face->boundary_id();\n\n        /* Skip periodic boundary faces; see above. */\n        if (id == Boundary::periodic)\n          continue;\n\n        /*\n         * Check for interior faces neighboring a cell with FE nothing:\n         */\n        const bool neighbor_cell_has_fe_nothing =                 //\n            treat_fe_nothing_as_boundary_ &&                      //\n            !face->at_boundary() &&                               //\n            cell->neighbor(f_index)->is_active() &&               //\n            !cell->neighbor(f_index)->is_artificial() &&          //\n            (dynamic_cast<const dealii::FE_Nothing<dim> *>(       //\n                 &cell->neighbor(f_index)->get_fe()) != nullptr); //\n\n        if (!neighbor_cell_has_fe_nothing && !face->at_boundary())\n          continue;\n\n        for (unsigned int j = 0; j < dofs_per_cell; ++j) {\n\n          if (!cell->get_fe().has_support_on_face(j, f_index))\n            continue;\n\n          const auto global_index = local_dof_indices[j];\n          const auto index = partitioner.global_to_local(global_index);\n\n          /* Skip irrelevant degrees of freedom: */\n          if (index >= n_locally_relevant_)\n            continue;\n\n          locally_relevant_boundary_indices.insert(index);\n        } /* j */\n      }   /* f */\n    }     /* cell */\n\n    /*\n     * Now, collect all coupling boundary pairs:\n     */\n\n    CouplingBoundaryPairs result;\n\n    for (const auto i : locally_relevant_boundary_indices) {\n\n      /* Only record pairs with a left index that is locally owned: */\n      if (i >= n_locally_owned_)\n        continue;\n\n      const unsigned int row_length = sparsity_pattern_simd_.row_length(i);\n\n      /* Skip all constrained degrees of freedom: */\n      if (row_length == 1)\n        continue;\n\n      const unsigned int *js = sparsity_pattern_simd_.columns(i);\n      constexpr auto simd_length = VectorizedArray<Number>::size();\n      /* skip diagonal: */\n      for (unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {\n        const auto j = *(i < n_locally_internal_ ? js + col_idx * simd_length\n                                                 : js + col_idx);\n\n        if (locally_relevant_boundary_indices.count(j) != 0) {\n          result.push_back({i, col_idx, j});\n        }\n      }\n    }\n\n    return result;\n  }\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/parabolic_module.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\nnamespace ryujin\n{\n  /**\n   * Compatibility using declaration that selects the correct\n   * ParabolicModule class type from the equation-specific Description\n   * proxy class.\n   *\n   * @ingroup ParabolicModule\n   */\n  template <typename Description, int dim, typename Number = double>\n  using ParabolicModule =\n      typename Description::template ParabolicModule<dim, Number>;\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/patterns_conversion.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2022 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <deal.II/base/parameter_acceptor.h>\n\n#ifndef DOXYGEN\n\nDEAL_II_NAMESPACE_OPEN\n\n\ntemplate <typename T>\nstruct ConversionHelper : std::false_type {\n};\n\n\n/**\n * Inject conversion rules into the deal.II namespace by specializing\n * Patterns::Tools::Convert for our custom enum classes.\n *\n * @ingroup Miscellaneous\n */\ntemplate <typename T>\nstruct Patterns::Tools::\n    Convert<T, typename std::enable_if_t<ConversionHelper<T>::value>> {\n\n  static std::unique_ptr<Patterns::PatternBase> to_pattern()\n  {\n    static const auto conversion_table = ConversionHelper<T>().conversion_table;\n    std::string s;\n    for (const auto &it : conversion_table)\n      s = s.empty() ? std::get<1>(it) : s + \"|\" + std::get<1>(it);\n    return std::make_unique<Patterns::Selection>(s);\n  }\n\n  static std::string\n  to_string(const T &t,\n            const Patterns::PatternBase & = *Convert<T>::to_pattern())\n  {\n    static const auto conversion_table = ConversionHelper<T>().conversion_table;\n    for (const auto &it : conversion_table) {\n      if (std::get<0>(it) == t)\n        return std::get<1>(it);\n    }\n\n    AssertThrow(false,\n                dealii::ExcMessage(\"Incomplete conversion table - unable to \"\n                                   \"identify matching string\"));\n  }\n\n  static T\n  to_value(const std::string &s,\n           const Patterns::PatternBase &pattern = *Convert<T>::to_pattern())\n  {\n    AssertThrow(pattern.match(s), ExcNoMatch(s, pattern.description()));\n\n    static const auto conversion_table = ConversionHelper<T>().conversion_table;\n    for (const auto &it : conversion_table) {\n      if (std::get<1>(it) == s)\n        return std::get<0>(it);\n    }\n\n    AssertThrow(false,\n                dealii::ExcMessage(\"Incomplete conversion table - unable to \"\n                                   \"identify matching value\"));\n  }\n};\n\nDEAL_II_NAMESPACE_CLOSE\n\n#define LIST(...) __VA_ARGS__\n\n/**\n * Shorthand macro for declaring \"string to enum class\" conversion rules\n * for the deal.II Patterns::Tools::Convert mechanism.\n *\n * @ingroup Miscellaneous\n */\n#define DECLARE_ENUM(type, s)                                                  \\\n  namespace dealii                                                             \\\n  {                                                                            \\\n    template <>                                                                \\\n    struct ConversionHelper<type> : std::true_type {                           \\\n      const std::vector<std::tuple<type, std::string>> conversion_table = {s}; \\\n    };                                                                         \\\n  }                                                                            \\\n  static_assert(true, \"\")\n\n#endif\n"
  },
  {
    "path": "source/postprocessor.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2023 by the ryujin authors\n//\n\n#include \"postprocessor.template.h\"\n#include <instantiate.h>\n\nnamespace ryujin\n{\n  /* instantiations */\n  template class Postprocessor<Description, 1, NUMBER>;\n  template class Postprocessor<Description, 2, NUMBER>;\n  template class Postprocessor<Description, 3, NUMBER>;\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/postprocessor.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"mpi_ensemble.h\"\n#include \"observer_pointer.h\"\n#include \"offline_data.h\"\n\n#include <deal.II/base/parameter_acceptor.h>\n\nnamespace ryujin\n{\n  /**\n   * The Postprocessor class implements a number of postprocessing\n   * primitives in particular for a scaled and normalized schlieren like\n   * plot, and a scaled and normalized magnitude of the vorticity. The\n   * normalization is computed as follows:\n   * \\f[\n   *   \\text{quantity}[i] = \\exp\\left(-\\beta \\frac{ |\\mathbf q_i| - \\min_k\n   * |\\mathbf q_k|}\n   *   {\\max_k |\\mathbf q_k| - \\min_k |\\mathbf q_k|}\\right),\n   * \\f]\n   * where \\f$\\mathbf q_i\\f$ is either\n   *  - the gradient of the density postprocessed as follows,\n   *    \\f[\n   *       \\mathbf q_i =  \\frac{1}{m_i}\\;\\sum_{j\\in \\mathcal{J}(i)}\n   * \\mathbf{c}_{ij} \\rho_j; \\f]\n   *  - the vorticity of the velocity field postprocessed as follows,\n   *    \\f[\n   *       \\mathbf q_i =  \\frac{1}{m_i}\\;\\sum_{j\\in \\mathcal{J}(i)}\n   * \\mathbf{c}_{ij} \\times \\mathbf{m}_j / \\rho_j. \\f]\n   *\n   * @ingroup TimeLoop\n   */\n  template <typename Description, int dim, typename Number = double>\n  class Postprocessor final : public dealii::ParameterAcceptor\n  {\n  public:\n    /**\n     * @name Typedefs and constexpr constants\n     */\n    //@{\n\n    using HyperbolicSystem = typename Description::HyperbolicSystem;\n    using ParabolicSystem = typename Description::ParabolicSystem;\n\n    using View =\n        typename Description::template HyperbolicSystemView<dim, Number>;\n\n    static constexpr auto problem_dimension = View::problem_dimension;\n\n    using state_type = typename View::state_type;\n\n    template <typename T>\n    using grad_type = dealii::Tensor<1, dim, T>;\n\n    template <typename T>\n    using curl_type = dealii::Tensor<1, dim == 2 ? 1 : dim, T>;\n\n    using StateVector = typename View::StateVector;\n\n    //@}\n    /**\n     * @name Constructor and setup\n     */\n    //@{\n\n    /**\n     * Constructor.\n     */\n    Postprocessor(const MPIEnsemble &mpi_ensemble,\n                  const OfflineData<dim, Number> &offline_data,\n                  const HyperbolicSystem &hyperbolic_system,\n                  const ParabolicSystem &parabolic_system,\n                  const std::string &subsection = \"/Postprocessor\");\n\n    /**\n     * Prepare Postprocessor. A call to @ref prepare() allocates temporary\n     * storage and is necessary before schedule_output() can be called.\n     *\n     * Calling prepare() allocates temporary storage for two additional\n     * scalar vectors of type OfflineData::scalar_type.\n     */\n    void prepare();\n\n    /**\n     * Returns the number of computed quantities.\n     */\n    unsigned int n_quantities() const\n    {\n      return quantities_.size();\n    }\n\n    /**\n     * A vector of strings for all component names.\n     */\n    const std::vector<std::string> component_names() const\n    {\n      return component_names_;\n    }\n\n    /**\n     * Reset computed normalization bounds. Calling this function will\n     * force a recomputation of the normalization bounds during the next\n     * call to compute().\n     */\n    void reset_bounds() const\n    {\n      bounds_.clear();\n    }\n\n    /**\n     * Given a state vector @p U and a file name prefix @p name, the\n     * current time @p t, and the current output cycle @p cycle) schedule a\n     * solution output.\n     *\n     * The function post-processes quantities synchronously, and (depending\n     * on configuration options) schedules the write-out asynchronously\n     * onto a background worker thread. This implies that @p U can again be\n     * modified once schedule_output() returned.\n     *\n     * The function requires MPI communication and is not reentrant.\n     */\n    void compute(const StateVector &state_vector) const;\n\n    /**\n     * Returns a reference to the quantities_ vector that has been filled\n     * by the compute() function.\n     */\n    ACCESSOR_READ_ONLY(quantities)\n\n\n  private:\n    /**\n     * @name Run time options\n     */\n    //@{\n\n    bool recompute_bounds_;\n    Number beta_;\n\n    std::vector<std::string> schlieren_quantities_;\n    std::vector<std::string> vorticity_quantities_;\n\n    //@}\n    /**\n     * @name Internal data\n     */\n    //@{\n\n    const MPIEnsemble &mpi_ensemble_;\n\n    dealii::ObserverPointer<const OfflineData<dim, Number>> offline_data_;\n    dealii::ObserverPointer<const HyperbolicSystem> hyperbolic_system_;\n    dealii::ObserverPointer<const ParabolicSystem> parabolic_system_;\n\n    std::vector<std::string> component_names_;\n    std::vector<std::pair<bool /*primitive*/, unsigned int>> schlieren_indices_;\n    std::vector<std::pair<bool /*primitive*/, unsigned int>> vorticity_indices_;\n\n    mutable std::vector<std::pair<Number, Number>> bounds_;\n    using ScalarHostVector = Vectors::ScalarHostVector<Number>;\n    mutable std::vector<ScalarHostVector> quantities_;\n    //@}\n  };\n\n} // namespace ryujin\n"
  },
  {
    "path": "source/postprocessor.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include \"loop.h\"\n#include \"postprocessor.h\"\n#include \"simd.h\"\n\n#include <deal.II/base/function_parser.h>\n#include <deal.II/numerics/data_out.h>\n#include <deal.II/numerics/vector_tools.h>\n\nnamespace ryujin\n{\n  template <typename Description, int dim, typename Number>\n  Postprocessor<Description, dim, Number>::Postprocessor(\n      const MPIEnsemble &mpi_ensemble,\n      const OfflineData<dim, Number> &offline_data,\n      const HyperbolicSystem &hyperbolic_system,\n      const ParabolicSystem &parabolic_system,\n      const std::string &subsection /*= \"Postprocessor\"*/)\n      : ParameterAcceptor(subsection)\n      , mpi_ensemble_(mpi_ensemble)\n      , offline_data_(&offline_data)\n      , hyperbolic_system_(&hyperbolic_system)\n      , parabolic_system_(&parabolic_system)\n  {\n    beta_ = 10.;\n    add_parameter(\"schlieren beta\",\n                  beta_,\n                  \"Beta factor used in the exponential scale for the schlieren \"\n                  \"and vorticity plots\");\n\n    recompute_bounds_ = true;\n    add_parameter(\n        \"schlieren recompute bounds\",\n        recompute_bounds_,\n        \"Recompute bounds for every output cycle. If set to false, bounds once \"\n        \"at the beginning and reused thereafter.\");\n\n    static_assert(View::component_names.size() > 0,\n                  \"Need at least one scalar quantitity\");\n    schlieren_quantities_.push_back(View::component_names[0]);\n\n    add_parameter(\n        \"schlieren quantities\",\n        schlieren_quantities_,\n        \"List of conserved quantities used for the schlieren postprocessor.\");\n\n    if constexpr (dim > 1) {\n      add_parameter(\n          \"vorticity quantities\",\n          vorticity_quantities_,\n          \"List of conserved quantities used for the vorticity postprocessor.\");\n    }\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void Postprocessor<Description, dim, Number>::prepare()\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"Postprocessor<dim, Number>::prepare()\" << std::endl;\n#endif\n\n    bounds_.clear();\n    component_names_.clear();\n    schlieren_indices_.clear();\n    vorticity_indices_.clear();\n\n    const auto populate = [&](const auto &strings,\n                              auto &indices,\n                              const auto &pre) {\n      const auto &cons = View::component_names;\n      const auto &prim = View::primitive_component_names;\n      for (const auto &entry : strings) {\n        bool found = false;\n        for (const auto &[is_primitive, names] :\n             {std::make_pair(false, cons), std::make_pair(true, prim)}) {\n          const auto pos = std::find(std::begin(names), std::end(names), entry);\n          if (!found && pos != std::end(names)) {\n            const auto index = std::distance(std::begin(names), pos);\n            indices.push_back(std::make_pair(is_primitive, index));\n            component_names_.push_back(pre + entry);\n            found = true;\n          }\n        }\n        AssertThrow(\n            found,\n            dealii::ExcMessage(\"Invalid component name »\" + entry + \"«\"));\n      }\n    };\n    populate(schlieren_quantities_, schlieren_indices_, \"schlieren_\");\n    populate(vorticity_quantities_, vorticity_indices_, \"vorticity_\");\n\n    const auto &partitioner = offline_data_->scalar_partitioner();\n\n    quantities_.resize(component_names_.size());\n    for (auto &it : quantities_)\n      it.reinit(partitioner);\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void Postprocessor<Description, dim, Number>::compute(\n      const StateVector &state_vector) const\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"Postprocessor<dim, Number>::compute()\" << std::endl;\n#endif\n\n    const auto &U = std::get<0>(state_vector);\n\n    using VA = dealii::VectorizedArray<Number>;\n\n    const auto &affine_constraints = offline_data_->affine_constraints();\n\n    const auto &sparsity_simd = offline_data_->sparsity_pattern_simd();\n    const auto &lumped_mass_matrix = offline_data_->lumped_mass_matrix();\n    const auto &cij_matrix = offline_data_->cij_matrix();\n\n    const unsigned int n_internal = offline_data_->n_locally_internal();\n    const unsigned int n_owned = offline_data_->n_locally_owned();\n\n    const unsigned int n_schlieren = schlieren_indices_.size();\n    Assert(n_schlieren == schlieren_quantities_.size(),\n           dealii::ExcInternalError());\n    const unsigned int n_vorticities = vorticity_indices_.size();\n    Assert(n_vorticities == vorticity_quantities_.size(),\n           dealii::ExcInternalError());\n    const unsigned int n_quantities = n_schlieren + n_vorticities;\n    Assert(n_quantities == quantities_.size(), dealii::ExcInternalError());\n    Assert(n_quantities == component_names_.size(), dealii::ExcInternalError());\n\n    /*\n     * Step 1: Compute quantities:\n     */\n\n    const auto body = [&](auto sentinel, unsigned int i) {\n      using T = decltype(sentinel);\n      constexpr unsigned int stride_size = get_stride_size<T>;\n\n      /* Skip constrained degrees of freedom: */\n      const unsigned int row_length = sparsity_simd.row_length(i);\n      if (row_length == 1)\n        return;\n\n      std::vector<grad_type<T>> local_schlieren_values(n_schlieren);\n      std::vector<curl_type<T>> local_vorticity_values(n_vorticities);\n\n      for (auto &it : local_schlieren_values)\n        it = grad_type<T>();\n      for (auto &it : local_vorticity_values)\n        it = curl_type<T>();\n\n      const unsigned int *js = sparsity_simd.columns(i);\n      for (unsigned int col_idx = 0; col_idx < row_length;\n           ++col_idx, js += stride_size) {\n\n        const auto U_j = U.template read_tensor<T>(js);\n        const auto view = hyperbolic_system_->template view<dim, T>();\n        const auto prim_j = view.to_primitive_state(U_j);\n\n        const auto c_ij = cij_matrix.template read_tensor<T>(i, col_idx);\n\n        unsigned int k = 0;\n        for (const auto &[is_primitive, index] : schlieren_indices_) {\n          local_schlieren_values[k++] -=\n              c_ij * (is_primitive ? prim_j[index] : U_j[index]);\n        }\n\n        k = 0;\n        for (const auto &[is_primitive, index] : vorticity_indices_) {\n          grad_type<T> q_j;\n          for (unsigned int d = 0; d < dim; ++d)\n            q_j[d] = (is_primitive ? prim_j[index + d] : U_j[index + d]);\n\n          if constexpr (dim == 2) {\n            local_vorticity_values[k++][0] -= cross_product_2d(c_ij) * q_j;\n          } else if constexpr (dim == 3) {\n            local_vorticity_values[k++] -= cross_product_3d(c_ij, q_j);\n          }\n        }\n      }\n\n      /* Populate quantities: */\n      const auto m_i = lumped_mass_matrix.template read_entry<T>(i);\n\n      unsigned int k = 0;\n      for (const auto &schlieren : local_schlieren_values) {\n        const auto value_i = schlieren.norm() / m_i;\n        write_entry<T>(quantities_[k++], value_i, i);\n      }\n      for (const auto &vorticity : local_vorticity_values) {\n        auto value_i = (dim == 2 ? vorticity[0] / m_i : vorticity.norm() / m_i);\n        write_entry<T>(quantities_[k++], value_i, i);\n      }\n    };\n\n    cpu_simd_loop<Number>(\"\", body, 0, n_internal, n_owned);\n\n    /*\n     * Step 2: Compute bounds and synchronize over MPI ranks:\n     */\n\n    /* Force recomputation of bounds: */\n    if (recompute_bounds_)\n      bounds_.clear();\n\n    if (bounds_.size() != n_quantities) {\n      bounds_.clear();\n      bounds_.resize(\n          n_quantities,\n          std::make_pair(Number(0.), std::numeric_limits<Number>::max()));\n\n      for (unsigned int d = 0; d < n_quantities; ++d) {\n        auto &[q_max, q_min] = bounds_[d];\n        for (unsigned int i = 0; i < n_owned; ++i) {\n          const auto q = quantities_[d].local_element(i);\n          q_max = std::max(q_max, std::abs(q));\n          q_min = std::min(q_min, std::abs(q));\n        }\n        q_max = dealii::Utilities::MPI::max(\n            q_max, mpi_ensemble_.ensemble_communicator());\n        q_min = dealii::Utilities::MPI::min(\n            q_min, mpi_ensemble_.ensemble_communicator());\n        Assert(q_max >= q_min, dealii::ExcInternalError());\n      }\n    }\n\n    /*\n     * Step 3: Normalize quantities on exponential scale:\n     */\n\n    {\n      constexpr Number eps = std::numeric_limits<Number>::epsilon();\n      constexpr Number floor = std::max(Number(1.0e-10), eps);\n\n      for (unsigned int d = 0; d < n_quantities; ++d) {\n        auto &[q_max, q_min] = bounds_[d];\n        for (unsigned int i = 0; i < n_owned; ++i) {\n          auto &q = quantities_[d].local_element(i);\n          /* clip off everything that is below the noise \"floor\": */\n          const auto ratio = std::max(Number(0.), std::abs(q) - q_min - floor) /\n                             std::max(q_max - q_min, eps);\n\n          const auto magnitude = Number(1.) - std::exp(-beta_ * ratio);\n          q = std::copysign(magnitude, q);\n        }\n      }\n    }\n\n    /*\n     * Step 4: Fix up constraints and distribute:\n     */\n\n    for (auto &it : quantities_) {\n      affine_constraints.distribute(it);\n      it.update_ghost_values();\n    }\n  }\n\n} // namespace ryujin\n"
  },
  {
    "path": "source/quantities.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2023 by the ryujin authors\n//\n\n#include \"quantities.template.h\"\n#include <instantiate.h>\n\nnamespace ryujin\n{\n  /* instantiations */\n  template class Quantities<Description, 1, NUMBER>;\n  template class Quantities<Description, 2, NUMBER>;\n  template class Quantities<Description, 3, NUMBER>;\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/quantities.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"mpi_ensemble.h\"\n#include \"observer_pointer.h\"\n#include \"offline_data.h\"\n\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/timer.h>\n#include <deal.II/lac/la_parallel_block_vector.h>\n#include <deal.II/lac/sparse_matrix.templates.h>\n#include <deal.II/lac/vector.h>\n\n#include <optional>\n\nnamespace ryujin\n{\n  /**\n   * A postprocessor class for quantities of interest.\n   *\n   * @ingroup TimeLoop\n   */\n  template <typename Description, int dim, typename Number = double>\n  class Quantities final : public dealii::ParameterAcceptor\n  {\n  public:\n    /**\n     * @name Typedefs and constexpr constants\n     */\n    //@{\n\n    using HyperbolicSystem = typename Description::HyperbolicSystem;\n    using ParabolicSystem = typename Description::ParabolicSystem;\n\n    using View =\n        typename Description::template HyperbolicSystemView<dim, Number>;\n\n    using state_type = typename View::state_type;\n\n    using StateVector = typename View::StateVector;\n\n    //@}\n    /**\n     * @name Constructor and setup\n     */\n\n    /**\n     * Constructor.\n     */\n    Quantities(const MPIEnsemble &mpi_ensemble,\n               const OfflineData<dim, Number> &offline_data,\n               const HyperbolicSystem &hyperbolic_system,\n               const ParabolicSystem &parabolic_system,\n               const std::string &subsection = \"/Quantities\");\n\n    /**\n     * Prepare evaluation. A call to @ref prepare() allocates temporary\n     * storage and is necessary before accumulate() and write_out() can be\n     * called.\n     *\n     * Calling prepare() allocates temporary storage for various boundary\n     * and interior maps. The storage requirement varies according to the\n     * supplied manifold descriptions.\n     *\n     * The string parameter @p name is used as base name for output files.\n     */\n    void prepare(const std::string &name);\n\n    /**\n     * Takes a state vector @p U at time t (obtained at the end of a full\n     * Strang step) and accumulates statistics for quantities of interests\n     * for all defined manifolds.\n     */\n    void accumulate(const StateVector &state_vector, const Number t);\n\n    /**\n     * Write quantities of interest to designated output files.\n     */\n    void write_out(const StateVector &state_vector,\n                   const Number t,\n                   unsigned int cycle);\n\n    //@}\n\n  private:\n    /**\n     * @name Run time options\n     */\n    //@{\n\n    std::vector<std::tuple<std::string, std::string, std::string>>\n        interior_manifolds_;\n\n    std::vector<std::tuple<std::string, std::string, std::string>>\n        boundary_manifolds_;\n\n    bool clear_temporal_statistics_on_writeout_;\n\n    //@}\n    /**\n     * @name Internal data\n     */\n    //@{\n\n    const MPIEnsemble &mpi_ensemble_;\n\n    dealii::ObserverPointer<const OfflineData<dim, Number>> offline_data_;\n    dealii::ObserverPointer<const HyperbolicSystem> hyperbolic_system_;\n    dealii::ObserverPointer<const ParabolicSystem> parabolic_system_;\n\n    /**\n     * A tuple describing (local) dof index, boundary normal, normal mass,\n     * boundary mass, boundary id, and position of the boundary degree of\n     * freedom.\n     *\n     * @fixme This type only differs from the one used in OfflineData by\n     * including a DoF index. It might be better to combine both.\n     */\n    using boundary_point =\n        std::tuple<dealii::types::global_dof_index /*local dof index*/,\n                   dealii::Tensor<1, dim, Number> /*normal*/,\n                   Number /*normal mass*/,\n                   Number /*boundary mass*/,\n                   dealii::types::boundary_id /*id*/,\n                   dealii::Point<dim>> /*position*/;\n\n    /**\n     * The boundary map.\n     */\n    std::map<std::string, std::vector<boundary_point>> boundary_maps_;\n\n    /**\n     * A tuple describing boundary values we are interested in: the\n     * primitive state and its second moment, boundary stresses and normal\n     * pressure force.\n     */\n    using boundary_value =\n        std::tuple<state_type /* primitive state */,\n                   state_type /* primitive state second moment */>;\n\n    /**\n     * Temporal statistics we store for each boundary manifold.\n     */\n    using boundary_statistic =\n        std::tuple<std::vector<boundary_value> /* values old */,\n                   std::vector<boundary_value> /* values new */,\n                   std::vector<boundary_value> /* values sum */,\n                   Number /* t old */,\n                   Number /* t new */,\n                   Number /* t sum */>;\n\n    /**\n     * Associated statistics for The boundary map.\n     */\n    std::map<std::string, boundary_statistic> boundary_statistics_;\n    std::map<std::string, std::vector<std::tuple<Number, boundary_value>>>\n        boundary_time_series_;\n\n    /**\n     * A tuple describing (local) dof index, mass, and position of an\n     * interior degree of freedom.\n     */\n    using interior_point =\n        std::tuple<dealii::types::global_dof_index /*local dof index*/,\n                   Number /*mass*/,\n                   dealii::Point<dim>> /*position*/;\n\n    /**\n     * The interior map.\n     */\n    std::map<std::string, std::vector<interior_point>> interior_maps_;\n\n    /**\n     * A tuple describing interior values we are interested in: the\n     * primitive state and its second moment.\n     */\n    using interior_value =\n        std::tuple<state_type /* primitive state */,\n                   state_type /* primitive state second moment */>;\n\n    /**\n     * Temporal statistics we store for each interior manifold.\n     */\n    using interior_statistic =\n        std::tuple<std::vector<interior_value> /* values old */,\n                   std::vector<interior_value> /* values new */,\n                   std::vector<interior_value> /* values sum */,\n                   Number /* t old */,\n                   Number /* t new */,\n                   Number /* t sum */>;\n\n    /**\n     * Associated statistics for The interior map.\n     */\n    std::map<std::string, interior_statistic> interior_statistics_;\n    std::map<std::string, std::vector<std::tuple<Number, interior_value>>>\n        interior_time_series_;\n\n    std::string base_name_;\n    bool first_cycle_;\n    std::optional<unsigned int> time_series_cycle_;\n\n    //@}\n    /**\n     * @name Internal methods\n     */\n    //@{\n\n    bool mesh_files_have_been_written_;\n\n    void write_mesh_files(unsigned int cycle);\n\n    void clear_statistics();\n\n    std::string header_;\n\n    template <typename point_type, typename value_type>\n    value_type internal_accumulate(const StateVector &state_vector,\n                                   const std::vector<point_type> &interior_map,\n                                   std::vector<value_type> &new_val);\n\n    template <typename value_type>\n    void internal_write_out(const std::string &file_name,\n                            const std::string &time_stamp,\n                            const std::vector<value_type> &values,\n                            const Number scale);\n\n    template <typename value_type>\n    void internal_write_out_time_series(\n        const std::string &file_name,\n        const std::vector<std::tuple<Number, value_type>> &values,\n        bool append);\n\n    //@}\n  };\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/quantities.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include \"quantities.h\"\n\n#include <deal.II/base/function_parser.h>\n#include <deal.II/base/mpi.templates.h>\n#include <deal.II/base/work_stream.h>\n#include <deal.II/dofs/dof_tools.h>\n\n#include <fstream>\n\nDEAL_II_NAMESPACE_OPEN\ntemplate <int rank, int dim, typename Number>\nbool operator<(const Tensor<rank, dim, Number> &left,\n               const Tensor<rank, dim, Number> &right)\n{\n  return std::lexicographical_compare(\n      left.begin_raw(), left.end_raw(), right.begin_raw(), right.end_raw());\n}\nDEAL_II_NAMESPACE_CLOSE\n\nnamespace ryujin\n{\n  using namespace dealii;\n\n  namespace\n  {\n    template <typename T>\n    const std::string &get_options_from_name(const T &manifolds,\n                                             const std::string &name)\n    {\n      const auto it =\n          std::find_if(manifolds.begin(),\n                       manifolds.end(),\n                       [&, name = std::cref(name)](const auto &element) {\n                         return std::get<0>(element) == name.get();\n                       });\n      Assert(it != manifolds.end(), dealii::ExcInternalError());\n      return std::get<2>(*it);\n    }\n  } // namespace\n\n\n  template <typename Description, int dim, typename Number>\n  Quantities<Description, dim, Number>::Quantities(\n      const MPIEnsemble &mpi_ensemble,\n      const OfflineData<dim, Number> &offline_data,\n      const HyperbolicSystem &hyperbolic_system,\n      const ParabolicSystem &parabolic_system,\n      const std::string &subsection /*= \"Quantities\"*/)\n      : ParameterAcceptor(subsection)\n      , mpi_ensemble_(mpi_ensemble)\n      , offline_data_(&offline_data)\n      , hyperbolic_system_(&hyperbolic_system)\n      , parabolic_system_(&parabolic_system)\n      , base_name_(\"\")\n      , mesh_files_have_been_written_(false)\n  {\n    add_parameter(\"interior manifolds\",\n                  interior_manifolds_,\n                  \"List of level set functions describing interior manifolds. \"\n                  \"The description is used to only output point values for \"\n                  \"vertices belonging to a certain level set. \"\n                  \"Format: '<name> : <level set formula> : <options> , [...] \"\n                  \"(options: time_averaged, space_averaged, instantaneous)\");\n\n    add_parameter(\"boundary manifolds\",\n                  boundary_manifolds_,\n                  \"List of level set functions describing boundary. The \"\n                  \"description is used to only output point values for \"\n                  \"boundary vertices belonging to a certain level set. \"\n                  \"Format: '<name> : <level set formula> : <options> , [...] \"\n                  \"(options: time_averaged, space_averaged, instantaneous)\");\n\n    clear_temporal_statistics_on_writeout_ = true;\n    add_parameter(\"clear statistics on writeout\",\n                  clear_temporal_statistics_on_writeout_,\n                  \"If set to true then all temporal statistics (for \"\n                  \"\\\"time_averaged\\\" quantities) accumulated so far are reset \"\n                  \"each time a writeout of quantities is performed\");\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void Quantities<Description, dim, Number>::prepare(const std::string &name)\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"Quantities<dim, Number>::prepare()\" << std::endl;\n#endif\n\n    base_name_ = name;\n\n    /* Force to write to a new time series file: */\n    time_series_cycle_.reset();\n\n    const unsigned int n_owned = offline_data_->n_locally_owned();\n    const auto &sparsity_simd = offline_data_->sparsity_pattern_simd();\n\n    /*\n     * Create interior maps and allocate statistics.\n     *\n     * We have to loop over the cells and populate the std::map interior_maps_.\n     */\n\n    interior_maps_.clear();\n    std::transform(\n        interior_manifolds_.begin(),\n        interior_manifolds_.end(),\n        std::inserter(interior_maps_, interior_maps_.end()),\n        [this, n_owned, &sparsity_simd](auto it) {\n          const auto &[name, expression, option] = it;\n          FunctionParser<dim> level_set_function(expression);\n\n          std::vector<interior_point> map;\n          std::map<int, interior_point> preliminary_map;\n\n          const auto &discretization = offline_data_->discretization();\n          const auto &dof_handler = offline_data_->dof_handler();\n\n          const auto support_points =\n              dof_handler.get_fe().get_unit_support_points();\n\n          std::vector<dealii::types::global_dof_index> local_dof_indices;\n\n          /* Loop over cells */\n          for (auto cell : dof_handler.active_cell_iterators()) {\n\n            /* skip if not locally owned */\n            if (!cell->is_locally_owned())\n              continue;\n\n            const unsigned int dofs_per_cell = cell->get_fe().n_dofs_per_cell();\n            local_dof_indices.resize(dofs_per_cell);\n            cell->get_active_or_mg_dof_indices(local_dof_indices);\n\n            const auto &mapping =\n                discretization.mapping()[cell->active_fe_index()];\n\n            for (unsigned int j = 0; j < dofs_per_cell; ++j) {\n\n              Point<dim> position =\n                  mapping.transform_unit_to_real_cell(cell, support_points[j]);\n\n              /*\n               * Insert index, interior mass value and position into\n               * a preliminary map if we satisfy level set condition.\n               */\n\n              if (std::abs(level_set_function.value(position)) > 1.e-12)\n                continue;\n\n              const auto global_index = local_dof_indices[j];\n              const auto index =\n                  offline_data_->scalar_partitioner()->global_to_local(\n                      global_index);\n\n              /* Skip constrained degrees of freedom: */\n              const unsigned int row_length = sparsity_simd.row_length(index);\n              if (row_length == 1)\n                continue;\n\n              if (index >= n_owned)\n                continue;\n\n              const Number interior_mass =\n                  offline_data_->lumped_mass_matrix().read_entry(index);\n              // FIXME: change to std::set\n              preliminary_map[index] = {index, interior_mass, position};\n            }\n          }\n\n          /*\n           * Now we populate the std::vector(interior_point) object called map.\n           */\n          // FIXME: use std::copy\n          for (const auto &[index, tuple] : preliminary_map) {\n            map.push_back(tuple);\n          }\n\n          return std::make_pair(name, map);\n        });\n\n    /*\n     * Create boundary maps and allocate statistics vector:\n     *\n     * We want to loop over the boundary_map() once and populate the map\n     * object boundary_maps_. We have to create a vector of\n     * boundary_manifolds.size() that holds a std::vector<boundary_point>\n     * for each map entry.\n     */\n\n    boundary_maps_.clear();\n    std::transform(\n        boundary_manifolds_.begin(),\n        boundary_manifolds_.end(),\n        std::inserter(boundary_maps_, boundary_maps_.end()),\n        [this, n_owned](auto it) {\n          const auto &[name, expression, option] = it;\n          FunctionParser<dim> level_set_function(expression);\n\n          std::vector<boundary_point> map;\n\n          for (const auto &entry : offline_data_->boundary_map()) {\n            // [i, normal, normal_mass, boundary_mass, id, position] = entry\n            const auto &i = std::get<0>(entry);\n\n            /* skip nonlocal */\n            if (i >= n_owned)\n              continue;\n\n            /* skip constrained */\n            if (offline_data_->affine_constraints().is_constrained(\n                    offline_data_->scalar_partitioner()->local_to_global(i)))\n              continue;\n\n            const auto &position = std::get<5>(entry);\n            if (std::abs(level_set_function.value(position)) < 1.e-12)\n              map.push_back(entry);\n          }\n          return std::make_pair(name, map);\n        });\n\n    /* Clear statistics: */\n    clear_statistics();\n\n    /* Make sure we output new mesh files: */\n    mesh_files_have_been_written_ = false;\n\n    /* Prepare header string: */\n    const auto &names = View::primitive_component_names;\n    header_ = std::accumulate(\n                  std::begin(names),\n                  std::end(names),\n                  std::string(),\n                  [](const std::string &description, const std::string &name) {\n                    return description.empty()\n                               ? (std::string(\"primitive state (\") + name)\n                               : (description + \", \" + name);\n                  }) +\n              \")\\t and 2nd moments\\n\";\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void\n  Quantities<Description, dim, Number>::write_mesh_files(unsigned int cycle)\n  {\n    /*\n     * Output interior maps:\n     */\n\n    for (const auto &[name, interior_map] : interior_maps_) {\n      /* Skip outputting the boundary map for spatial averages. */\n      const auto &options = get_options_from_name(interior_manifolds_, name);\n      if (options.find(\"instantaneous\") == std::string::npos &&\n          options.find(\"time_averaged\") == std::string::npos)\n        continue;\n\n      /*\n       * FIXME: This currently distributes boundary maps to all MPI ranks.\n       * This is unnecessarily wasteful. Ideally, we should do MPI IO with\n       * only MPI ranks participating who actually have boundary values.\n       */\n\n      const auto received = Utilities::MPI::gather(\n          mpi_ensemble_.ensemble_communicator(), interior_map);\n\n      if (Utilities::MPI::this_mpi_process(\n              mpi_ensemble_.ensemble_communicator()) == 0) {\n\n        std::ofstream output(base_name_ + \"-\" + name + \"-R\" +\n                             Utilities::to_string(cycle, 4) + \"-points.dat\");\n\n        output << std::scientific << std::setprecision(14);\n\n        output << \"#\\n# position\\tinterior mass\\n\";\n\n        unsigned int rank = 0;\n        for (const auto &entries : received) {\n          output << \"# rank \" << rank++ << \"\\n\";\n          for (const auto &entry : entries) {\n            const auto &[index, mass_i, x_i] = entry;\n            output << x_i << \"\\t\" << mass_i << \"\\n\";\n          } /*entry*/\n        }   /*entries*/\n\n        output << std::flush;\n      }\n    }\n\n    /*\n     * Output boundary maps:\n     */\n\n    for (const auto &[name, boundary_map] : boundary_maps_) {\n      /* Skip outputting the boundary map for spatial averages. */\n      const auto &options = get_options_from_name(boundary_manifolds_, name);\n      if (options.find(\"instantaneous\") == std::string::npos &&\n          options.find(\"time_averaged\") == std::string::npos)\n        continue;\n\n      /*\n       * FIXME: This currently distributes boundary maps to all MPI ranks.\n       * This is unnecessarily wasteful. Ideally, we should do MPI IO with\n       * only MPI ranks participating who actually have boundary values.\n       */\n\n      const auto received = Utilities::MPI::gather(\n          mpi_ensemble_.ensemble_communicator(), boundary_map);\n\n      if (Utilities::MPI::this_mpi_process(\n              mpi_ensemble_.ensemble_communicator()) == 0) {\n\n        std::ofstream output(base_name_ + \"-\" + name + \"-R\" +\n                             Utilities::to_string(cycle, 4) + \"-points.dat\");\n\n        output << std::scientific << std::setprecision(14);\n\n        output << \"#\\n# position\\tnormal\\tnormal mass\\tboundary mass\\n\";\n\n        unsigned int rank = 0;\n        for (const auto &entries : received) {\n          output << \"# rank \" << rank++ << \"\\n\";\n          for (const auto &entry : entries) {\n            const auto &[index, n_i, nm_i, bm_i, id, x_i] = entry;\n            output << x_i << \"\\t\" << n_i << \"\\t\" << nm_i << \"\\t\" << bm_i\n                   << \"\\n\";\n          } /*entry*/\n        }   /*entries*/\n\n        output << std::flush;\n      }\n    }\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void Quantities<Description, dim, Number>::clear_statistics()\n  {\n    const auto reset = [](const auto &manifold_map, auto &statistics_map) {\n      for (const auto &[name, data_map] : manifold_map) {\n        const auto n_entries = data_map.size();\n        auto &[val_old, val_new, val_sum, t_old, t_new, t_sum] =\n            statistics_map[name];\n        val_old.resize(n_entries);\n        val_new.resize(n_entries);\n        val_sum.resize(n_entries);\n        t_old = t_new = t_sum = 0.;\n      }\n    };\n\n    /* Clear statistics and time series: */\n\n    interior_statistics_.clear();\n    reset(interior_maps_, interior_statistics_);\n    interior_time_series_.clear();\n\n    boundary_statistics_.clear();\n    reset(boundary_maps_, boundary_statistics_);\n    boundary_time_series_.clear();\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  template <typename point_type, typename value_type>\n  value_type Quantities<Description, dim, Number>::internal_accumulate(\n      const StateVector &state_vector,\n      const std::vector<point_type> &points_vector,\n      std::vector<value_type> &val_new)\n  {\n    const auto &U = std::get<0>(state_vector);\n\n    value_type spatial_average;\n    Number mass_sum = Number(0.);\n\n    std::transform(\n        points_vector.begin(),\n        points_vector.end(),\n        val_new.begin(),\n        [&](auto point) -> value_type {\n          const auto i = std::get<0>(point);\n          /*\n           * Small trick to get the correct index for retrieving the\n           * boundary mass.\n           */\n          constexpr auto index =\n              std::is_same_v<point_type, interior_point> ? 1 : 3;\n          const auto mass_i = std::get<index>(point);\n\n          const auto U_i = U.read_tensor(i);\n          const auto view = hyperbolic_system_->template view<dim, Number>();\n          const auto primitive_state = view.to_primitive_state(U_i);\n\n          value_type result;\n          std::get<0>(result) = primitive_state;\n          /* Compute second moments of the primitive state: */\n          std::get<1>(result) = schur_product(primitive_state, primitive_state);\n\n          mass_sum += mass_i;\n          std::get<0>(spatial_average) += mass_i * std::get<0>(result);\n          std::get<1>(spatial_average) += mass_i * std::get<1>(result);\n\n          return result;\n        });\n\n    /* synchronize MPI ranks (MPI Barrier): */\n\n    mass_sum =\n        Utilities::MPI::sum(mass_sum, mpi_ensemble_.ensemble_communicator());\n\n    std::get<0>(spatial_average) = Utilities::MPI::sum(\n        std::get<0>(spatial_average), mpi_ensemble_.ensemble_communicator());\n    std::get<1>(spatial_average) = Utilities::MPI::sum(\n        std::get<1>(spatial_average), mpi_ensemble_.ensemble_communicator());\n\n    /* take average: */\n\n    std::get<0>(spatial_average) /= mass_sum;\n    std::get<1>(spatial_average) /= mass_sum;\n\n    return spatial_average;\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  template <typename value_type>\n  void Quantities<Description, dim, Number>::internal_write_out(\n      const std::string &file_name,\n      const std::string &time_stamp,\n      const std::vector<value_type> &values,\n      const Number scale)\n  {\n    /*\n     * FIXME: This currently distributes interior maps to all MPI ranks.\n     * This is unnecessarily wasteful. Ideally, we should do MPI IO with\n     * only MPI ranks participating who actually have interior values.\n     */\n\n    const auto received =\n        Utilities::MPI::gather(mpi_ensemble_.ensemble_communicator(), values);\n\n    if (Utilities::MPI::this_mpi_process(\n            mpi_ensemble_.ensemble_communicator()) == 0) {\n\n      std::ofstream output(file_name);\n      output << std::scientific << std::setprecision(14);\n      output << time_stamp << \"# \" << header_;\n\n      unsigned int rank = 0;\n      for (const auto &entries : received) {\n        output << \"# rank \" << rank++ << \"\\n\";\n        for (const auto &entry : entries) {\n          const auto &[state, state_square] = entry;\n          output << scale * state << \"\\t\" << scale * state_square << \"\\n\";\n        } /*entry*/\n      }   /*entries*/\n\n      output << std::flush;\n    }\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  template <typename value_type>\n  void Quantities<Description, dim, Number>::internal_write_out_time_series(\n      const std::string &file_name,\n      const std::vector<std::tuple<Number, value_type>> &values,\n      bool append)\n  {\n    if (Utilities::MPI::this_mpi_process(\n            mpi_ensemble_.ensemble_communicator()) == 0) {\n      std::ofstream output;\n      output << std::scientific << std::setprecision(14);\n\n      if (append) {\n        output.open(file_name, std::ofstream::out | std::ofstream::app);\n      } else {\n        output.open(file_name, std::ofstream::out | std::ofstream::trunc);\n        output << \"# time t\\t\" << header_;\n      }\n\n      for (const auto &entry : values) {\n        const auto t = std::get<0>(entry);\n        const auto &[state, state_square] = std::get<1>(entry);\n\n        output << t << \"\\t\" << state << \"\\t\" << state_square << \"\\n\";\n      }\n\n      output << std::flush;\n      output.close();\n    }\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void Quantities<Description, dim, Number>::accumulate(\n      const StateVector &state_vector, const Number t)\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"Quantities<dim, Number>::accumulate()\" << std::endl;\n#endif\n\n    const auto accumulate = [&](const auto &point_maps,\n                                const auto &manifolds,\n                                auto &statistics,\n                                auto &time_series) {\n      for (const auto &[name, point_map] : point_maps) {\n\n        /* Find the correct option string in manifolds */\n        const auto &options = get_options_from_name(manifolds, name);\n\n        /* skip if we don't average in space or time: */\n        if (options.find(\"time_averaged\") == std::string::npos &&\n            options.find(\"space_averaged\") == std::string::npos)\n          continue;\n\n        auto &[val_old, val_new, val_sum, t_old, t_new, t_sum] =\n            statistics[name];\n\n        std::swap(t_old, t_new);\n        std::swap(val_old, val_new);\n\n        /* accumulate new values */\n\n        const auto spatial_average =\n            internal_accumulate(state_vector, point_map, val_new);\n\n        /* Average in time with trapezoidal rule: */\n\n        if (RYUJIN_UNLIKELY(t_old == Number(0.) && t_new == Number(0.))) {\n          /* We have not accumulated any statistics yet: */\n          t_old = t - 1.;\n          t_new = t;\n\n        } else {\n\n          t_new = t;\n          const Number tau = t_new - t_old;\n\n          for (std::size_t i = 0; i < val_sum.size(); ++i) {\n            std::get<0>(val_sum[i]) += 0.5 * tau * std::get<0>(val_old[i]);\n            std::get<0>(val_sum[i]) += 0.5 * tau * std::get<0>(val_new[i]);\n            std::get<1>(val_sum[i]) += 0.5 * tau * std::get<1>(val_old[i]);\n            std::get<1>(val_sum[i]) += 0.5 * tau * std::get<1>(val_new[i]);\n          }\n          t_sum += tau;\n        }\n\n        /* Record average in space: */\n        time_series[name].push_back({t, spatial_average});\n      }\n    };\n\n    accumulate(interior_maps_,\n               interior_manifolds_,\n               interior_statistics_,\n               interior_time_series_);\n\n    accumulate(boundary_maps_,\n               boundary_manifolds_,\n               boundary_statistics_,\n               boundary_time_series_);\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void Quantities<Description, dim, Number>::write_out(\n      const StateVector &state_vector, const Number t, unsigned int cycle)\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"Quantities<dim, Number>::write_out()\" << std::endl;\n#endif\n\n    /*\n     * First, write out mesh files if this hasn't happened yet.\n     */\n    if (!mesh_files_have_been_written_) {\n      write_mesh_files(cycle);\n      mesh_files_have_been_written_ = true;\n    }\n\n    /*\n     * Next write out instantaneous and time_averaged maps, and flush the\n     * space_averaged values to the corresponding log files:\n     */\n\n    const auto write_out = [&](const auto &point_maps,\n                               const auto &manifolds,\n                               auto &statistics,\n                               auto &time_series) {\n      for (const auto &[name, point_map] : point_maps) {\n\n        /* Find the correct option string in manifolds */\n        const auto &options = get_options_from_name(manifolds, name);\n\n        const auto prefix =\n            base_name_ + \"-\" + name + \"-R\" + Utilities::to_string(cycle, 4);\n\n        /*\n         * Compute and output instantaneous field:\n         */\n\n        if (options.find(\"instantaneous\") != std::string::npos) {\n\n          const std::string file_name = prefix + \"-instantaneous.dat\";\n\n          auto &[val_old, val_new, val_sum, t_old, t_new, t_sum] =\n              statistics[name];\n\n          std::stringstream time_stamp;\n          time_stamp << std::scientific << std::setprecision(14);\n          time_stamp << \"# at t = \" << t << std::endl;\n\n          /* We have not computed any updated statistics yet: */\n\n          if (options.find(\"time_averaged\") == std::string::npos &&\n              options.find(\"space_averaged\") == std::string::npos)\n            internal_accumulate(state_vector, point_map, val_new);\n          else\n            AssertThrow(t_new == t, dealii::ExcInternalError());\n\n          internal_write_out(file_name, time_stamp.str(), val_new, Number(1.));\n        }\n\n        /*\n         * Output time averaged field:\n         */\n\n        if (options.find(\"time_averaged\") != std::string::npos) {\n\n          const std::string file_name = prefix + \"-time_averaged.dat\";\n\n          auto &[val_old, val_new, val_sum, t_old, t_new, t_sum] =\n              statistics[name];\n\n          /* Check whether we have accumulated any statistics yet: */\n          if (t_sum != Number(0.)) {\n            std::stringstream time_stamp;\n            time_stamp << std::scientific << std::setprecision(14);\n            time_stamp << \"# averaged from t = \" << t_new - t_sum\n                       << \" to t = \" << t_new << std::endl;\n\n            internal_write_out(\n                file_name, time_stamp.str(), val_sum, Number(1.) / t_sum);\n          }\n        }\n\n        /*\n         * Output space averaged field:\n         */\n\n        if (options.find(\"space_averaged\") != std::string::npos) {\n          bool append = true;\n          if (!time_series_cycle_.has_value()) {\n            time_series_cycle_ = cycle;\n            append = false;\n          }\n\n          const auto file_name =\n              base_name_ + \"-\" + name + \"-R\" +\n              Utilities::to_string(time_series_cycle_.value(), 4) +\n              \"-space_averaged_time_series.dat\";\n\n          auto &series = time_series[name];\n          internal_write_out_time_series(file_name, series, /*append*/ append);\n          series.clear();\n        }\n      }\n    };\n\n    write_out(interior_maps_,\n              interior_manifolds_,\n              interior_statistics_,\n              interior_time_series_);\n\n    write_out(boundary_maps_,\n              boundary_manifolds_,\n              boundary_statistics_,\n              boundary_time_series_);\n\n    if (clear_temporal_statistics_on_writeout_)\n      clear_statistics();\n  }\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/scalar_conservation/CMakeLists.txt",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2023 - 2024 by the ryujin authors\n##\n\nadd_library(obj_scalar_conservation OBJECT\n  equation_dispatch.cc\n  flux_library.cc\n  initial_state_library.cc\n  limiter.cc\n  riemann_solver.cc\n  )\nset_target_properties(obj_scalar_conservation PROPERTIES LINKER_LANGUAGE CXX)\ntarget_link_libraries(obj_scalar_conservation obj_common)\ndeal_ii_setup_target(obj_scalar_conservation)\n# Propagate the current source directory with PUBLIC visibility\ntarget_include_directories(obj_scalar_conservation PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})\n"
  },
  {
    "path": "source/scalar_conservation/Makefile",
    "content": "default: all\n.PHONY: default\n\n%:\n\t@cd .. && make $@\n.PHONY: %\n"
  },
  {
    "path": "source/scalar_conservation/description.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"../stub_parabolic_module.h\"\n#include \"../stub_parabolic_system.h\"\n#include \"hyperbolic_system.h\"\n#include \"indicator.h\"\n#include \"limiter.h\"\n#include \"riemann_solver.h\"\n\nnamespace ryujin\n{\n  namespace ScalarConservation\n  {\n    /**\n     * This struct contains all equation specific classes describing the\n     * chosen hyperbolic system, the indicator, the limiter and\n     * (approximate) Riemann solver.\n     *\n     * A scalar conservation equation for a scalar unknown u with a\n     * user-specified flux depending on the state u.\n     *\n     * The parabolic subsystem is chosen to be the identity.\n     *\n     * @note We group all of these templates together in this struct so\n     * that we only need to add a single template parameter to the all the\n     * algorithm classes, such as HyperbolicModule.\n     *\n     * @ingroup ScalarConservationEquations\n     */\n    struct Description {\n      using HyperbolicSystem = ScalarConservation::HyperbolicSystem;\n\n      template <int dim, typename Number = double>\n      using HyperbolicSystemView =\n          ScalarConservation::HyperbolicSystemView<dim, Number>;\n\n      using ParabolicSystem = ryujin::StubParabolicSystem;\n\n      template <int dim, typename Number = double>\n      using ParabolicModule =\n          ryujin::StubParabolicModule<Description, dim, Number>;\n\n      template <int dim, typename Number = double>\n      using Indicator = ScalarConservation::Indicator<dim, Number>;\n\n      template <int dim, typename Number = double>\n      using Limiter = ScalarConservation::Limiter<dim, Number>;\n\n      template <int dim, typename Number = double>\n      using RiemannSolver = ScalarConservation::RiemannSolver<dim, Number>;\n    };\n  } // namespace ScalarConservation\n} // namespace ryujin\n"
  },
  {
    "path": "source/scalar_conservation/equation_dispatch.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2024 by the ryujin authors\n//\n\n#include \"description.h\"\n\n#include <compile_time_options.h>\n#include <equation_dispatch.h>\n\nnamespace ryujin\n{\n  namespace ScalarConservation\n  {\n    Dispatch<Description, NUMBER> dispatch_instance(\"scalar conservation\");\n  } // namespace ScalarConservation\n} // namespace ryujin\n"
  },
  {
    "path": "source/scalar_conservation/flux.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"convenience_macros.h\"\n\n#include <deal.II/base/function.h>\n#include <deal.II/base/parameter_acceptor.h>\n\n#include <string>\n\nnamespace ryujin\n{\n  namespace FluxLibrary\n  {\n    /**\n     * A small abstract base class to group configuration options for the\n     * flux.\n     *\n     * This function derives directly from dealii::Function<1>. Derived\n     * classes must thus implement the value() and gradient() methods.\n     *\n     * @ingroup ScalarConservation\n     */\n    class Flux : public dealii::ParameterAcceptor\n    {\n    public:\n      /**\n       * Constructor taking flux name @p name and a subsection @p\n       * subsection as an argument. The dealii::ParameterAcceptor is\n       * initialized with the subsubsection `subsection + \"/\" + name`.\n       */\n      Flux(const std::string &name, const std::string &subsection)\n          /* simply default to three components... */\n          : ParameterAcceptor(subsection + \"/\" + name)\n          , name_(name)\n      {\n        derivative_approximation_delta_ =\n            1.e4 * std::numeric_limits<double>::epsilon();\n      }\n\n\n      /**\n       * Return the flux f(u) for the given state @p U and direction\n       * @p direction.\n       */\n      virtual double value(double state, unsigned int direction) const = 0;\n\n\n      /**\n       * Return the gradient f'(u) of the flux for the given state @p u and\n       * direction @p direction.\n       */\n      virtual double gradient(double state, unsigned int direction) const = 0;\n\n      /**\n       * The name of the flux function\n       */\n      ACCESSOR_READ_ONLY(name);\n\n      /**\n       * Return an appropriate step size for approximating a differential\n       * with a central difference quotient. This value is used internally\n       * in the Riemann solver to switch between a central difference\n       * formula and the \"true\" gradient() for computing the Roe average.\n       *\n       * The value should be set to be of the same order than any\n       * discretization used for computing the gradient(). The default\n       * value is 1e4 times the machine epsilon and is appropriate if\n       * gradient() is known and therefore implemented exactly.\n       */\n      ACCESSOR_READ_ONLY(derivative_approximation_delta)\n\n      /**\n       * A string showing a detailed formula of the chosen flux function\n       * (such as \"f(u)=0.5*u*u\").\n       */\n      ACCESSOR_READ_ONLY(flux_formula);\n\n    protected:\n      double derivative_approximation_delta_;\n      std::string flux_formula_;\n\n    private:\n      const std::string name_;\n    };\n\n  } // namespace FluxLibrary\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/scalar_conservation/flux_burgers.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"flux.h\"\n\nnamespace ryujin\n{\n  namespace FluxLibrary\n  {\n    /**\n     * A generic flux description parsed from a user-provided string\n     *\n     * @ingroup ScalarConservation\n     */\n    class Burgers : public Flux\n    {\n    public:\n      Burgers(const std::string &subsection)\n          : Flux(\"burgers\", subsection)\n      {\n        flux_formula_ = \"f(u)={0.5u^2}\";\n      }\n\n\n      double value(const double state,\n                   const unsigned int /*direction*/) const override\n      {\n        return 0.5 * state * state;\n      }\n\n\n      double gradient(const double state,\n                      const unsigned int /*direction*/) const override\n      {\n        return state;\n      }\n    };\n  } // namespace FluxLibrary\n} // namespace ryujin\n"
  },
  {
    "path": "source/scalar_conservation/flux_function.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"flux.h\"\n\n#include <deal.II/base/function_parser.h>\n\n#include <boost/algorithm/string/classification.hpp>\n#include <boost/algorithm/string/split.hpp>\n\n#include <numeric>\n\nnamespace ryujin\n{\n  namespace FluxLibrary\n  {\n    /**\n     * A generic flux description parsed from a user-provided string\n     *\n     * @ingroup ScalarConservation\n     */\n    class Function : public Flux\n    {\n    public:\n      Function(const std::string &subsection)\n          : Flux(\"function\", subsection)\n      {\n        expression_ = \"0.5*u*u\";\n        add_parameter(\"expression\",\n                      expression_,\n                      \"A function expression of the flux as a function of \"\n                      \"state used to create a muparser object to evaluate the \"\n                      \"flux. For two, or three dimensional fluxes, components \"\n                      \"are separated with a semicolon (;).\");\n\n        this->derivative_approximation_delta_ = 1.0e-10;\n        add_parameter(\"derivative approximation delta\",\n                      this->derivative_approximation_delta_,\n                      \"Step size of the central difference quotient to compute \"\n                      \"an approximation of the flux derivative\");\n\n        /*\n         * Set up the muparser object with the final flux description from\n         * the parameter file:\n         */\n        const auto set_up_muparser = [this] {\n          std::vector<std::string> split_expressions;\n          boost::split(split_expressions, expression_, boost::is_any_of(\";\"));\n\n          const auto size = split_expressions.size();\n\n          Assert(0 < size && size <= 3,\n                 dealii::ExcMessage(\n                     \"user specified flux description must be either one, two, \"\n                     \"or three strings separated by a comma\"));\n          flux_function_ = std::make_unique<dealii::FunctionParser<1>>(\n              size, 0.0, this->derivative_approximation_delta_);\n          flux_function_->initialize({\"u\"}, split_expressions, {});\n\n          flux_formula_ = \"f(u)={\" + expression_ + \"}\";\n        };\n\n        set_up_muparser();\n        ParameterAcceptor::parse_parameters_call_back.connect(set_up_muparser);\n      }\n\n\n      double value(const double state,\n                   const unsigned int direction) const override\n      {\n        return flux_function_->value(dealii::Point<1>(state), direction);\n      }\n\n\n      double gradient(const double state,\n                      const unsigned int direction) const override\n      {\n        return flux_function_->gradient(dealii::Point<1>(state), direction)[0];\n      }\n\n\n    private:\n      std::string expression_;\n\n      std::unique_ptr<dealii::FunctionParser<1>> flux_function_;\n    };\n  } // namespace FluxLibrary\n} // namespace ryujin\n"
  },
  {
    "path": "source/scalar_conservation/flux_kpp.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"flux.h\"\n\nnamespace ryujin\n{\n  namespace FluxLibrary\n  {\n    /**\n     * A generic flux description parsed from a user-provided string\n     *\n     * @ingroup ScalarConservation\n     */\n    class KPP : public Flux\n    {\n    public:\n      KPP(const std::string &subsection)\n          : Flux(\"kpp\", subsection)\n      {\n        flux_formula_ = \"f(u)={sin(u),cos(u)}\";\n      }\n\n\n      double value(const double state,\n                   const unsigned int direction) const override\n      {\n        switch (direction) {\n        case 0:\n          return std::sin(state);\n        case 1:\n          return std::cos(state);\n        default:\n          AssertThrow(false,\n                      dealii::ExcMessage(\n                          \"KPP is only defined in (1 or) 2 space dimensions\"));\n          __builtin_trap();\n        }\n      }\n\n\n      double gradient(const double state,\n                      const unsigned int direction) const override\n      {\n        switch (direction) {\n        case 0:\n          return std::cos(state);\n        case 1:\n          return -std::sin(state);\n        default:\n          AssertThrow(false,\n                      dealii::ExcMessage(\n                          \"KPP is only defined in (1 or) 2 space dimensions\"));\n          __builtin_trap();\n        }\n      }\n    };\n  } // namespace FluxLibrary\n} // namespace ryujin\n"
  },
  {
    "path": "source/scalar_conservation/flux_library.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 by the ryujin authors\n//\n\n#include \"flux_library.h\"\n\n#include \"flux_burgers.h\"\n#include \"flux_function.h\"\n#include \"flux_kpp.h\"\n\nnamespace ryujin\n{\n  namespace FluxLibrary\n  {\n    /**\n     * Populate a given container with all equation of states defined in\n     * this namespace.\n     *\n     * @ingroup EulerEquations\n     */\n\n    void populate_flux_list(flux_list_type &flux_list,\n                            const std::string &subsection)\n    {\n      auto add = [&](auto &&object) { flux_list.emplace(std::move(object)); };\n\n      add(std::make_shared<Burgers>(subsection));\n      add(std::make_shared<Function>(subsection));\n      add(std::make_shared<KPP>(subsection));\n    }\n  } // namespace FluxLibrary\n} // namespace ryujin\n"
  },
  {
    "path": "source/scalar_conservation/flux_library.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"flux.h\"\n\nnamespace ryujin\n{\n  namespace FluxLibrary\n  {\n    using flux_list_type = std::set<std::shared_ptr<Flux>>;\n\n    /**\n     * Populate a given container with all fluxes defined in this\n     * namespace.\n     *\n     * @ingroup ScalarConservation\n     */\n    void populate_flux_list(flux_list_type &flux_list,\n                            const std::string &subsection);\n\n  } // namespace FluxLibrary\n} // namespace ryujin\n"
  },
  {
    "path": "source/scalar_conservation/hyperbolic_system.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"flux_library.h\"\n\n#include <convenience_macros.h>\n#include <discretization.h>\n#include <loop.h>\n#include <multicomponent_vector.h>\n#include <patterns_conversion.h>\n#include <simd.h>\n#include <state_vector.h>\n\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/tensor.h>\n\n#include <array>\n\nnamespace ryujin\n{\n  namespace ScalarConservation\n  {\n    template <int dim, typename Number>\n    class HyperbolicSystemView;\n\n    /**\n     * A scalar conservation equation for a state \\f$u\\f$ with a user\n     * specified flux \\f$f(u)\\f$.\n     *\n     * @ingroup ScalarConservationEquations\n     */\n    class HyperbolicSystem final : public dealii::ParameterAcceptor\n    {\n    public:\n      /**\n       * The name of the hyperbolic system as a string.\n       */\n      static inline std::string problem_name = \"Scalar conservation equation\";\n\n      /**\n       * Constructor.\n       */\n      HyperbolicSystem(const std::string &subsection = \"/HyperbolicSystem\");\n\n      /**\n       * Return a view on the Hyperbolic System for a given dimension @p\n       * dim and choice of number type @p Number (which can be a scalar\n       * float, or double, as well as a VectorizedArray holding packed\n       * scalars.\n       */\n      template <int dim, typename Number>\n      auto view() const\n      {\n        return HyperbolicSystemView<dim, Number>{*this};\n      }\n\n      /**\n       * Part of step 1 of the hyperbolic update step: Compute \"precomputed\n       * values\" and fill into the state vector.\n       *\n       * @note The method does not update the ghost range of the state\n       * vector. The precomputed part has to be synchronized by explicitly\n       * calling the update ghost values function.\n       */\n      template <int dim, typename ScalarNumber>\n      void fill_precomputed_values(\n          const OfflineData<dim, ScalarNumber> &offline_data,\n          typename HyperbolicSystemView<dim, ScalarNumber>::StateVector\n              &state_vector,\n          const bool skip_constrained_dofs = true) const;\n\n    private:\n      /**\n       * @name Runtime parameters, internal fields, methods, and friends\n       */\n      //@{\n      std::string flux_;\n\n      FluxLibrary::flux_list_type flux_list_;\n      using Flux = FluxLibrary::Flux;\n      std::shared_ptr<Flux> selected_flux_;\n\n      template <int dim, typename Number>\n      friend class HyperbolicSystemView;\n      //@}\n    }; /* HyperbolicSystem */\n\n\n    /**\n     * A view on the HyperbolicSystem for a given dimension @p dim and\n     * choice of number type @p Number (which can be a scalar float, or\n     * double, as well as a VectorizedArray holding packed scalars.\n     */\n    template <int dim, typename Number>\n    class HyperbolicSystemView\n    {\n    public:\n      /**\n       * Constructor taking a reference to the underlying\n       * HyperbolicSystem\n       */\n      HyperbolicSystemView(const HyperbolicSystem &hyperbolic_system)\n          : hyperbolic_system_(hyperbolic_system)\n      {\n      }\n\n      /**\n       * Create a modified view from the current one:\n       */\n      template <int dim2, typename Number2>\n      auto view() const\n      {\n        return HyperbolicSystemView<dim2, Number2>{hyperbolic_system_};\n      }\n\n      /**\n       * The underlying scalar number type.\n       */\n      using ScalarNumber = typename get_value_type<Number>::type;\n\n      /**\n       * @name Access to runtime parameters\n       */\n      //@{\n\n      DEAL_II_ALWAYS_INLINE inline const std::string &flux() const\n      {\n        return hyperbolic_system_.flux_;\n      }\n\n      DEAL_II_ALWAYS_INLINE inline ScalarNumber\n      derivative_approximation_delta() const\n      {\n        const auto &flux = hyperbolic_system_.selected_flux_;\n        return ScalarNumber(flux->derivative_approximation_delta());\n      }\n\n      //@}\n      /**\n       * @name Low-level access to the flux function parser:\n       */\n      //@{\n\n      /**\n       * For a given state \\f$u\\f$ compute the flux \\f$f(u)\\f$.\n       */\n      DEAL_II_ALWAYS_INLINE inline dealii::Tensor<1, dim, Number>\n      flux_function(const Number &u) const;\n\n      /**\n       * For a given state \\f$u\\f$ compute the flux gradient \\f$f'(u)\\f$.\n       */\n      DEAL_II_ALWAYS_INLINE inline dealii::Tensor<1, dim, Number>\n      flux_gradient_function(const Number &u) const;\n\n      //@}\n      /**\n       * @name Internal data\n       */\n      //@{\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n    public:\n      //@}\n      /**\n       * @name Types and constexpr constants\n       */\n      //@{\n\n      /**\n       * The dimension of the state space.\n       */\n      static constexpr unsigned int problem_dimension = 1;\n\n      /**\n       * Storage type for a (conserved) state vector \\f$\\boldsymbol U\\f$.\n       */\n      using state_type = dealii::Tensor<1, problem_dimension, Number>;\n\n      /**\n       * Storage type for the flux \\f$\\mathbf{f}\\f$.\n       */\n      using flux_type =\n          dealii::Tensor<1, problem_dimension, dealii::Tensor<1, dim, Number>>;\n\n      /**\n       * The storage type used for flux contributions.\n       */\n      using flux_contribution_type = flux_type;\n\n      /**\n       * An array holding all component names of the conserved state as a\n       * string.\n       */\n      static inline const auto component_names =\n          std::array<std::string, problem_dimension>{\"u\"};\n\n      /**\n       * An array holding all component names of the primitive state as a\n       * string.\n       */\n      static inline const auto primitive_component_names =\n          std::array<std::string, problem_dimension>{\"u\"};\n\n      /**\n       * The number of precomputed values.\n       */\n      static constexpr unsigned int n_precomputed_values = 2 * dim;\n\n      /**\n       * Array type used for precomputed values.\n       */\n      using precomputed_type = std::array<Number, n_precomputed_values>;\n\n      /**\n       * An array holding all component names of the precomputed values.\n       */\n      static inline const auto precomputed_names =\n          []() -> std::array<std::string, n_precomputed_values> {\n        if constexpr (dim == 1)\n          return {\"f\", \"df\"};\n        else if constexpr (dim == 2)\n          return {\"f_1\", \"f_2\", \"df_1\", \"df_2\"};\n        else if constexpr (dim == 3)\n          return {\"f_1\", \"f_2\", \"f_3\", \"df_1\", \"df_2\", \"df_3\"};\n        __builtin_trap();\n      }();\n\n      /**\n       * The number of precomputed initial values.\n       */\n      static constexpr unsigned int n_initial_precomputed_values = 0;\n\n      /**\n       * Array type used for precomputed initial values.\n       */\n      using initial_precomputed_type =\n          std::array<Number, n_initial_precomputed_values>;\n\n      /**\n       * An array holding all component names of the precomputed values.\n       */\n      static inline const auto initial_precomputed_names =\n          std::array<std::string, n_initial_precomputed_values>{};\n\n      /**\n       * A compound state vector.\n       */\n      using StateVector = Vectors::\n          StateVector<ScalarNumber, problem_dimension, n_precomputed_values>;\n\n      /**\n       * MulticomponentVector for storing the hyperbolic state vector:\n       */\n      using HyperbolicVector =\n          Vectors::MultiComponentVector<ScalarNumber, problem_dimension>;\n\n      /**\n       * MulticomponentVector for storing a vector of precomputed states:\n       */\n      using PrecomputedVector =\n          Vectors::MultiComponentVector<ScalarNumber, n_precomputed_values>;\n\n      /**\n       * MulticomponentVector for storing a vector of precomputed initial\n       * states:\n       */\n      using InitialPrecomputedVector =\n          Vectors::MultiComponentVector<ScalarNumber,\n                                        n_initial_precomputed_values>;\n\n      //@}\n      /**\n       * @name Computing derived physical quantities\n       */\n      //@{\n\n      /**\n       * Return the scalar value stored in the state \"vector\". Given the\n       * fact that a scalar conservation equation is indeed scalar this\n       * simply unwraps the Tensor from the state_type and returns the\n       * one and only entry.\n       */\n      static Number state(const state_type &U);\n\n      /**\n       * For a given state <code>U</code>, compute the square entropy\n       * \\f[\n       *   \\eta = 1/2 u^2.\n       * \\f]\n       */\n      Number square_entropy(const Number &u) const;\n\n      /**\n       * For a given state <code>U</code>, compute the derivative of the\n       * square entropy\n       * \\f[\n       *   \\eta' = u.\n       * \\f]\n       */\n      Number square_entropy_derivative(const Number &u) const;\n\n      /**\n       * For a given state <code>U</code>, compute the Krŭzkov entropy\n       * \\f[\n       *   \\eta = |u-k|.\n       * \\f]\n       */\n      Number kruzkov_entropy(const Number &k, const Number &u) const;\n\n      /**\n       * For a given state <code>U</code>, compute the derivative of the\n       * Krŭzkov entropy:\n       * \\f[\n       *   \\eta' = \\text{sgn}(u-k).\n       * \\f]\n       */\n      Number kruzkov_entropy_derivative(const Number &k, const Number &u) const;\n\n      /**\n       * Returns whether the state @p U is admissible. If @p U is a\n       * vectorized state then @p U is admissible if all vectorized\n       * values are admissible.\n       */\n      bool is_admissible(const state_type & /*U*/) const\n      {\n        return true;\n      }\n\n      //@}\n      /**\n       * @name Special functions for boundary states\n       */\n      //@{\n\n      /**\n       * Apply boundary conditions.\n       */\n      template <typename Lambda>\n      state_type\n      apply_boundary_conditions(const dealii::types::boundary_id id,\n                                const state_type &U,\n                                const dealii::Tensor<1, dim, Number> &normal,\n                                const Lambda &get_dirichlet_data) const;\n\n      //@}\n      /**\n       * @name Flux computations\n       */\n      //@{\n\n      /**\n       * Helper function that given a @p precomputed_state constructs a\n       * dealii::Tensor with the flux @f$f(u)@f$.\n       */\n      dealii::Tensor<1, dim, Number>\n      construct_flux_tensor(const precomputed_type &precomputed_state) const;\n\n      /**\n       * Helper function that given a @p precomputed_state constructs a\n       * dealii::Tensor with the gradient of the flux @f$f(u)@f$ with\n       * respect to the state @f$u@f$.\n       */\n      dealii::Tensor<1, dim, Number> construct_flux_gradient_tensor(\n          const precomputed_type &precomputed_state) const;\n\n      /**\n       * Given a state @p U_i and an index @p i compute flux contributions.\n       *\n       * Intended usage:\n       * ```\n       * Indicator<dim, Number> indicator;\n       * for (unsigned int i = n_internal; i < n_owned; ++i) {\n       *   // ...\n       *   const auto flux_i = flux_contribution(precomputed..., i, U_i);\n       *   for (unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {\n       *     // ...\n       *     const auto flux_j = flux_contribution(precomputed..., js, U_j);\n       *     const auto flux_ij = flux_divergence(flux_i, flux_j, c_ij);\n       *   }\n       * }\n       * ```\n       *\n       * For the Euler equations we simply compute <code>f(U_i)</code>.\n       */\n      flux_contribution_type\n      flux_contribution(const PrecomputedVector &pv,\n                        const InitialPrecomputedVector & /*piv*/,\n                        const unsigned int i,\n                        const state_type & /*U_i*/) const;\n\n      flux_contribution_type\n      flux_contribution(const PrecomputedVector &pv,\n                        const InitialPrecomputedVector & /*piv*/,\n                        const unsigned int *js,\n                        const state_type & /*U_j*/) const;\n\n      /**\n       * Given flux contributions @p flux_i and @p flux_j compute the flux\n       * <code>(-f(U_i) - f(U_j)</code>\n       */\n      state_type\n      flux_divergence(const flux_contribution_type &flux_i,\n                      const flux_contribution_type &flux_j,\n                      const dealii::Tensor<1, dim, Number> &c_ij) const;\n\n      /** The low-order and high-order fluxes are the same */\n      static constexpr bool have_high_order_flux = false;\n\n      state_type high_order_flux_divergence(\n          const flux_contribution_type &,\n          const flux_contribution_type &,\n          const dealii::Tensor<1, dim, Number> &c_ij) const = delete;\n\n      //@}\n      /**\n       * @name Computing stencil source terms\n       */\n      //@{\n\n      /** We do not have source terms */\n      static constexpr bool have_source_terms = false;\n\n      state_type nodal_source(const PrecomputedVector &pv,\n                              const unsigned int i,\n                              const state_type &U_i,\n                              const ScalarNumber tau) const = delete;\n\n      state_type nodal_source(const PrecomputedVector &pv,\n                              const unsigned int *js,\n                              const state_type &U_j,\n                              const ScalarNumber tau) const = delete;\n\n      //@}\n      /**\n       * @name State transformations\n       */\n      //@{\n\n      /**\n       * Given a state vector associated with a different spatial\n       * dimensions than the current one, return an \"expanded\" version of\n       * the state vector associated with @a dim spatial dimensions where\n       * the momentum vector of the conserved state @p state is expaned\n       * with zeros to a total length of @a dim entries.\n       *\n       * @note @a dim has to be larger or equal than the dimension of the\n       * @a ST vector.\n       */\n      template <typename ST>\n      state_type expand_state(const ST &state) const\n      {\n        return state;\n      }\n\n      /**\n       * Given a primitive state [rho, u_1, ..., u_d, p] return a conserved\n       * state\n       */\n      state_type from_primitive_state(const state_type &primitive_state) const\n      {\n        return primitive_state;\n      }\n\n      /**\n       * Given a conserved state return a primitive state [rho, u_1, ..., u_d,\n       * p]\n       */\n      state_type to_primitive_state(const state_type &state) const\n      {\n        return state;\n      }\n\n      /**\n       * Transform the current state according to a  given operator\n       * @p lambda acting on a @a dim dimensional momentum (or velocity)\n       * vector.\n       */\n      template <typename Lambda>\n      state_type apply_galilei_transform(const state_type &state,\n                                         const Lambda & /*lambda*/) const\n      {\n        return state;\n      }\n\n    }; /* HyperbolicSystemView */\n\n\n    /*\n     * -------------------------------------------------------------------------\n     * Inline definitions\n     * -------------------------------------------------------------------------\n     */\n\n\n    inline HyperbolicSystem::HyperbolicSystem(const std::string &subsection)\n        : ParameterAcceptor(subsection)\n    {\n      flux_ = \"burgers\";\n      add_parameter(\"flux\",\n                    flux_,\n                    \"The scalar flux. Valid names are given by any of the \"\n                    \"subsections defined below\");\n\n      /*\n       * And finally populate the flux list with all flux configurations\n       * defined in the FluxLibrary namespace:\n       */\n      FluxLibrary::populate_flux_list(flux_list_, subsection);\n\n      const auto populate_functions = [this]() {\n        bool initialized = false;\n        for (auto &it : flux_list_)\n\n          /* Populate flux functions: */\n          if (it->name() == flux_) {\n            selected_flux_ = it;\n            it->parse_parameters_call_back();\n            problem_name = \"Scalar conservation equation (\" + it->name() +\n                           \": \" + it->flux_formula() + \")\";\n            initialized = true;\n            break;\n          }\n\n        AssertThrow(initialized,\n                    dealii::ExcMessage(\n                        \"Could not find a flux description with name \\\"\" +\n                        flux_ + \"\\\"\"));\n      };\n\n      ParameterAcceptor::parse_parameters_call_back.connect(populate_functions);\n      populate_functions();\n    }\n\n\n    template <int dim, typename ScalarNumber>\n    inline void HyperbolicSystem::fill_precomputed_values(\n        const OfflineData<dim, ScalarNumber> &offline_data,\n        typename HyperbolicSystemView<dim, ScalarNumber>::StateVector\n            &state_vector,\n        const bool skip_constrained_dofs) const\n    {\n      const unsigned int n_internal = offline_data.n_locally_internal();\n      const unsigned int n_owned = offline_data.n_locally_owned();\n      const auto &sparsity_simd = offline_data.sparsity_pattern_simd();\n      using VA = dealii::VectorizedArray<ScalarNumber>;\n\n      const auto &U = std::get<0>(state_vector);\n      auto &precomputed = std::get<1>(state_vector);\n\n      const auto body = [&](auto sentinel, unsigned int i) {\n        using T = decltype(sentinel);\n        using View = HyperbolicSystemView<dim, T>;\n        using precomputed_type = typename View::precomputed_type;\n\n        const unsigned int row_length = sparsity_simd.row_length(i);\n        if (skip_constrained_dofs && row_length == 1)\n          return;\n\n        const auto U_i = U.template read_tensor<T>(i);\n        const auto view = this->view<dim, T>();\n        const auto u_i = view.state(U_i);\n        const auto f_i = view.flux_function(u_i);\n        const auto df_i = view.flux_gradient_function(u_i);\n\n        precomputed_type prec_i;\n        for (unsigned int k = 0; k < View::n_precomputed_values / 2; ++k) {\n          prec_i[k] = f_i[k];\n          prec_i[dim + k] = df_i[k];\n        }\n\n        precomputed.template write_tensor<T>(prec_i, i);\n      };\n\n      cpu_simd_loop<ScalarNumber>(\"time_step_1\", body, 0, n_internal, n_owned);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline dealii::Tensor<1, dim, Number>\n    HyperbolicSystemView<dim, Number>::flux_function(const Number &u) const\n    {\n      const auto &flux = hyperbolic_system_.selected_flux_;\n      dealii::Tensor<1, dim, Number> result;\n\n      /* This access by calling into value() repeatedly is terrible: */\n      for (unsigned int k = 0; k < dim; ++k) {\n        if constexpr (std::is_same_v<ScalarNumber, Number>) {\n          result[k] = flux->value(u, k);\n        } else {\n          for (unsigned int s = 0; s < Number::size(); ++s) {\n            result[k][s] = flux->value(u[s], k);\n          }\n        }\n      }\n\n      return result;\n    }\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline dealii::Tensor<1, dim, Number>\n    HyperbolicSystemView<dim, Number>::flux_gradient_function(\n        const Number &u) const\n    {\n      const auto &flux = hyperbolic_system_.selected_flux_;\n      dealii::Tensor<1, dim, Number> result;\n\n      /* This access by calling into value() repeatedly is terrible: */\n      for (unsigned int k = 0; k < dim; ++k) {\n        if constexpr (std::is_same_v<ScalarNumber, Number>) {\n          result[k] = flux->gradient(u, k);\n        } else {\n          for (unsigned int s = 0; s < Number::size(); ++s) {\n            result[k][s] = flux->gradient(u[s], k);\n          }\n        }\n      }\n\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::state(const state_type &U)\n    {\n      return U[0];\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::square_entropy(const Number &u) const\n    {\n      return ScalarNumber(0.5) * u * u;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::square_entropy_derivative(\n        const Number &u) const\n    {\n      return u;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::kruzkov_entropy(const Number &k,\n                                                       const Number &u) const\n    {\n      return std::abs(k - u);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::kruzkov_entropy_derivative(\n        const Number &k, const Number &u) const\n    {\n      constexpr auto gte = dealii::SIMDComparison::greater_than_or_equal;\n      // return sgn(u-k):\n      return dealii::compare_and_apply_mask<gte>(u, k, Number(1.), Number(-1.));\n    }\n\n\n    template <int dim, typename Number>\n    template <typename Lambda>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::apply_boundary_conditions(\n        dealii::types::boundary_id id,\n        const state_type &U,\n        const dealii::Tensor<1, dim, Number> & /*normal*/,\n        const Lambda &get_dirichlet_data) const -> state_type\n    {\n      state_type result = U;\n\n      if (id == Boundary::dirichlet) {\n        result = get_dirichlet_data();\n\n      } else if (id == Boundary::dirichlet_momentum) {\n        AssertThrow(false,\n                    dealii::ExcMessage(\n                        \"Invalid boundary ID »Boundary::dirichlet_momentum«, \"\n                        \"enforcing Dirichlet boundary conditions on a momentum \"\n                        \"is not possible for scalar conservation equations.\"));\n\n      } else if (id == Boundary::dirichlet_velocity) {\n        AssertThrow(false,\n                    dealii::ExcMessage(\n                        \"Invalid boundary ID »Boundary::dirichlet_velocity«, \"\n                        \"enforcing Dirichlet boundary conditions on a momentum \"\n                        \"is not possible for scalar conservation equations.\"));\n\n      } else if (id == Boundary::slip) {\n        AssertThrow(\n            false,\n            dealii::ExcMessage(\"Invalid boundary ID »Boundary::slip«, slip \"\n                               \"boundary conditions are unavailable for scalar \"\n                               \"conservation equations.\"));\n        __builtin_trap();\n\n      } else if (id == Boundary::no_slip) {\n        AssertThrow(\n            false,\n            dealii::ExcMessage(\"Invalid boundary ID »Boundary::no_slip«, \"\n                               \"no-slip boundary conditions are unavailable \"\n                               \"for scalar conservation equations.\"));\n        __builtin_trap();\n\n      } else if (id == Boundary::dynamic) {\n        AssertThrow(\n            false,\n            dealii::ExcMessage(\"Invalid boundary ID »Boundary::dynamic«, \"\n                               \"dynamic boundary conditions are unavailable \"\n                               \"for scalar conservation equations.\"));\n        __builtin_trap();\n\n      } else {\n        AssertThrow(false, dealii::ExcNotImplemented());\n      }\n\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline dealii::Tensor<1, dim, Number>\n    HyperbolicSystemView<dim, Number>::construct_flux_tensor(\n        const precomputed_type &precomputed) const\n    {\n      dealii::Tensor<1, dim, Number> result;\n\n      if constexpr (dim == 1) {\n        const auto &[f, df] = precomputed;\n        result[0] = f;\n\n      } else if constexpr (dim == 2) {\n        const auto &[f_1, f_2, df_1, df_2] = precomputed;\n        result[0] = f_1;\n        result[1] = f_2;\n\n      } else if constexpr (dim == 3) {\n        const auto &[f_1, f_2, f_3, df_1, df_2, df_3] = precomputed;\n        result[0] = f_1;\n        result[1] = f_2;\n        result[2] = f_3;\n      }\n\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline dealii::Tensor<1, dim, Number>\n    HyperbolicSystemView<dim, Number>::construct_flux_gradient_tensor(\n        const precomputed_type &precomputed) const\n    {\n      dealii::Tensor<1, dim, Number> result;\n\n      if constexpr (dim == 1) {\n        const auto &[f, df] = precomputed;\n        result[0] = df;\n\n      } else if constexpr (dim == 2) {\n        const auto &[f_1, f_2, df_1, df_2] = precomputed;\n        result[0] = df_1;\n        result[1] = df_2;\n\n      } else if constexpr (dim == 3) {\n        const auto &[f_1, f_2, f_3, df_1, df_2, df_3] = precomputed;\n        result[0] = df_1;\n        result[1] = df_2;\n        result[2] = df_3;\n      }\n\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::flux_contribution(\n        const PrecomputedVector &pv,\n        const InitialPrecomputedVector & /*piv*/,\n        const unsigned int i,\n        const state_type & /*U_i*/) const -> flux_contribution_type\n    {\n      /* The flux contribution is a rank 2 tensor, thus a little dance: */\n      flux_contribution_type result;\n      result[0] = construct_flux_tensor(\n          pv.template read_tensor<Number, precomputed_type>(i));\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::flux_contribution(\n        const PrecomputedVector &pv,\n        const InitialPrecomputedVector & /*piv*/,\n        const unsigned int *js,\n        const state_type & /*U_j*/) const -> flux_contribution_type\n    {\n      /* The flux contribution is a rank 2 tensor, thus a little dance: */\n      flux_contribution_type result;\n      result[0] = construct_flux_tensor(\n          pv.template read_tensor<Number, precomputed_type>(js));\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::flux_divergence(\n        const flux_contribution_type &flux_i,\n        const flux_contribution_type &flux_j,\n        const dealii::Tensor<1, dim, Number> &c_ij) const -> state_type\n    {\n      return -contract(add(flux_i, flux_j), c_ij);\n    }\n\n  } // namespace ScalarConservation\n} // namespace ryujin\n"
  },
  {
    "path": "source/scalar_conservation/indicator.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"hyperbolic_system.h\"\n\n#include <multicomponent_vector.h>\n#include <simd.h>\n\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/vectorization.h>\n\n\nnamespace ryujin\n{\n  namespace ScalarConservation\n  {\n    template <typename ScalarNumber = double>\n    class IndicatorParameters : public dealii::ParameterAcceptor\n    {\n    public:\n      IndicatorParameters(const std::string &subsection = \"/Indicator\")\n          : ParameterAcceptor(subsection)\n      {\n        evc_factor_ = ScalarNumber(1.);\n        add_parameter(\"evc factor\",\n                      evc_factor_,\n                      \"Factor for scaling the entropy viscocity commuator\");\n      }\n\n      ACCESSOR_READ_ONLY(evc_factor);\n\n    private:\n      ScalarNumber evc_factor_;\n    };\n\n\n    /**\n     * An suitable indicator strategy that is used to form the preliminary\n     * high-order update.\n     *\n     * @ingroup ScalarConservationEquations\n     */\n    template <int dim, typename Number = double>\n    class Indicator\n    {\n    public:\n      /**\n       * @name Typedefs and constexpr constants\n       */\n      //@{\n\n      using View = HyperbolicSystemView<dim, Number>;\n\n      using ScalarNumber = typename View::ScalarNumber;\n\n      static constexpr auto problem_dimension = View::problem_dimension;\n\n      using state_type = typename View::state_type;\n\n      using flux_type = typename View::flux_type;\n\n      using precomputed_type = typename View::precomputed_type;\n\n      using PrecomputedVector = typename View::PrecomputedVector;\n\n      using Parameters = IndicatorParameters<ScalarNumber>;\n\n      //@}\n      /**\n       * @name Stencil-based computation of indicators\n       *\n       * Intended usage:\n       * ```\n       * Indicator<dim, Number> indicator;\n       * for (unsigned int i = n_internal; i < n_owned; ++i) {\n       *   // ...\n       *   indicator.reset(i, U_i);\n       *   for (unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {\n       *     // ...\n       *     indicator.accumulate(js, U_j, c_ij);\n       *   }\n       *   indicator.alpha(hd_i);\n       * }\n       * ```\n       */\n      //@{\n\n      /**\n       * Constructor taking a HyperbolicSystem instance as argument\n       */\n      Indicator(const HyperbolicSystem &hyperbolic_system,\n                const Parameters &parameters,\n                const PrecomputedVector &precomputed_values)\n          : hyperbolic_system(hyperbolic_system)\n          , parameters(parameters)\n          , precomputed_values(precomputed_values)\n      {\n      }\n\n      /**\n       * Reset temporary storage and initialize for a new row corresponding\n       * to state vector U_i.\n       */\n      void reset(const unsigned int i, const state_type &U_i);\n\n      /**\n       * When looping over the sparsity row, add the contribution associated\n       * with the neighboring state U_j.\n       */\n      void accumulate(const unsigned int *js,\n                      const state_type &U_j,\n                      const dealii::Tensor<1, dim, Number> &c_ij);\n\n      /**\n       * Return the computed alpha_i value.\n       */\n      Number alpha(const Number h_i) const;\n\n      //@}\n\n    private:\n      /**\n       * @name\n       */\n      //@{\n\n      const HyperbolicSystem &hyperbolic_system;\n      const Parameters &parameters;\n      const PrecomputedVector &precomputed_values;\n\n      Number u_i;\n      Number u_abs_max;\n      dealii::Tensor<1, dim, Number> f_i;\n      Number left;\n      Number right;\n      //@}\n    };\n\n\n    /*\n     * -------------------------------------------------------------------------\n     * Inline definitions\n     * -------------------------------------------------------------------------\n     */\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline void\n    Indicator<dim, Number>::reset(const unsigned int i, const state_type &U_i)\n    {\n      /* entropy viscosity commutator: */\n\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      const auto prec_i =\n          precomputed_values.template read_tensor<Number, precomputed_type>(i);\n\n      u_i = view.state(U_i);\n      u_abs_max = std::abs(u_i);\n      f_i = view.construct_flux_tensor(prec_i);\n      left = 0.;\n      right = 0.;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline void Indicator<dim, Number>::accumulate(\n        const unsigned int *js,\n        const state_type &U_j,\n        const dealii::Tensor<1, dim, Number> &c_ij)\n    {\n      /* entropy viscosity commutator: */\n\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      const auto prec_j =\n          precomputed_values.template read_tensor<Number, precomputed_type>(js);\n\n      const auto u_j = view.state(U_j);\n      u_abs_max = std::max(u_abs_max, std::abs(u_j));\n      const auto d_eta_j = view.kruzkov_entropy_derivative(u_i, u_j);\n      const auto f_j = view.construct_flux_tensor(prec_j);\n\n      left += d_eta_j * (f_j * c_ij);\n      right += d_eta_j * (f_i * c_ij);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    Indicator<dim, Number>::alpha(const Number hd_i) const\n    {\n      Number numerator = left - right;\n      Number denominator = std::abs(left) + std::abs(right);\n\n      const auto regularization =\n          Number(100. * std::numeric_limits<ScalarNumber>::min());\n\n      const auto quotient =\n          std::abs(numerator) /\n          (denominator + std::max(hd_i * std::abs(u_abs_max), regularization));\n\n      return std::min(Number(1.), parameters.evc_factor() * quotient);\n    }\n\n  } // namespace ScalarConservation\n} // namespace ryujin\n"
  },
  {
    "path": "source/scalar_conservation/initial_state_function.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"hyperbolic_system.h\"\n#include <initial_state_library.h>\n\n#include <deal.II/base/function_parser.h>\n\nnamespace ryujin\n{\n  namespace ScalarConservation\n  {\n    struct Description;\n\n    /**\n     * Initial state defined by a user provided function\n     *\n     * @ingroup ScalarConservationEquations\n     */\n    template <int dim, typename Number>\n    class Function : public InitialState<Description, dim, Number>\n    {\n    public:\n      using View = HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n\n      Function(const HyperbolicSystem &hyperbolic_system,\n               const std::string subsection)\n          : InitialState<Description, dim, Number>(\"function\", subsection)\n          , hyperbolic_system(hyperbolic_system)\n      {\n        expression_ = \"0.25 * x\";\n        this->add_parameter(\"expression\",\n                            expression_,\n                            \"A function expression for the initial state\");\n\n        /*\n         * Set up the muparser object with the final flux description from\n         * the parameter file:\n         */\n        const auto set_up_muparser = [this] {\n          /*\n           * This variant of the constructor initializes the function\n           * parser with support for a time-dependent description involving\n           * a variable »t«:\n           */\n          function_ =\n              std::make_unique<dealii::FunctionParser<dim>>(expression_);\n        };\n\n        set_up_muparser();\n        this->parse_parameters_call_back.connect(set_up_muparser);\n      }\n\n      state_type compute(const dealii::Point<dim> &point, Number t) final\n      {\n        function_->set_time(t);\n        state_type result;\n        result[0] = function_->value(point);\n        return result;\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system;\n\n      std::string expression_;\n      std::unique_ptr<dealii::FunctionParser<dim>> function_;\n    };\n  } // namespace ScalarConservation\n} // namespace ryujin\n"
  },
  {
    "path": "source/scalar_conservation/initial_state_library.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 by the ryujin authors\n//\n\n#include \"initial_state_library.template.h\"\n\nnamespace ryujin\n{\n  template class InitialStateLibrary<Description, 1, NUMBER>;\n  template class InitialStateLibrary<Description, 2, NUMBER>;\n  template class InitialStateLibrary<Description, 3, NUMBER>;\n} // namespace ryujin\n"
  },
  {
    "path": "source/scalar_conservation/initial_state_library.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <initial_state_library.h>\n\n#include \"description.h\"\n#include \"initial_state_function.h\"\n#include \"initial_state_uniform.h\"\n\nnamespace ryujin\n{\n  using namespace ScalarConservation;\n\n  template <int dim, typename Number>\n  class InitialStateLibrary<Description, dim, Number>\n  {\n  public:\n    using HyperbolicSystem = typename Description::HyperbolicSystem;\n    using ParabolicSystem = typename Description::ParabolicSystem;\n\n    using View =\n        typename Description::template HyperbolicSystemView<dim, Number>;\n\n    using initial_state_list_type =\n        std::set<std::unique_ptr<InitialState<Description, dim, Number>>>;\n\n    static void\n    populate_initial_state_list(initial_state_list_type &initial_state_list,\n                                const HyperbolicSystem &h,\n                                const ParabolicSystem & /*p*/,\n                                const std::string &s)\n    {\n      auto add = [&](auto &&object) {\n        initial_state_list.emplace(std::move(object));\n      };\n\n      add(std::make_unique<Function<dim, Number>>(h, s));\n      add(std::make_unique<Uniform<dim, Number>>(h, s));\n    }\n  };\n} // namespace ryujin\n"
  },
  {
    "path": "source/scalar_conservation/initial_state_uniform.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2022 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"hyperbolic_system.h\"\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  namespace ScalarConservation\n  {\n    struct Description;\n\n    /**\n     * Uniform initial state defined by a given primitive state.\n     *\n     * @ingroup ScalarConservationEquations\n     */\n    template <int dim, typename Number>\n    class Uniform : public InitialState<Description, dim, Number>\n    {\n    public:\n      using View = HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n\n      Uniform(const HyperbolicSystem &hyperbolic_system,\n              const std::string subsection)\n          : InitialState<Description, dim, Number>(\"uniform\", subsection)\n          , hyperbolic_system(hyperbolic_system)\n      {\n        primitive_[0] = 1.0;\n        this->add_parameter(\n            \"primitive state\", primitive_, \"Initial 1d primitive state\");\n      }\n\n      state_type compute(const dealii::Point<dim> & /*point*/,\n                         Number /*t*/) final\n      {\n        const auto view = hyperbolic_system.view<dim, Number>();\n        return view.from_primitive_state(view.expand_state(primitive_));\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system;\n\n      state_type primitive_;\n    };\n  } // namespace ScalarConservation\n} // namespace ryujin\n"
  },
  {
    "path": "source/scalar_conservation/instantiate.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 by the ryujin authors\n//\n\n#ifndef RYUJIN_INCLUDE_INSTANTIATION_ONCE\n#define RYUJIN_INCLUDE_INSTANTIATION_ONCE\n#else\n#error Instantiation files can only be included once.\n#endif\n\n#include \"description.h\"\n\nnamespace ryujin\n{\n  using ScalarConservation::Description;\n} // namespace ryujin\n"
  },
  {
    "path": "source/scalar_conservation/limiter.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2023 by the ryujin authors\n//\n\n#include \"limiter.template.h\"\n\nusing namespace dealii;\n\nnamespace ryujin\n{\n  namespace ScalarConservation\n  {\n    /* instantiations */\n\n    template class Limiter<1, NUMBER>;\n    template class Limiter<2, NUMBER>;\n    template class Limiter<3, NUMBER>;\n\n    template class Limiter<1, dealii::VectorizedArray<NUMBER>>;\n    template class Limiter<2, dealii::VectorizedArray<NUMBER>>;\n    template class Limiter<3, dealii::VectorizedArray<NUMBER>>;\n  } // namespace ScalarConservation\n} // namespace ryujin\n"
  },
  {
    "path": "source/scalar_conservation/limiter.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"hyperbolic_system.h\"\n\n#include <multicomponent_vector.h>\n#include <simd.h>\n\nnamespace ryujin\n{\n  namespace ScalarConservation\n  {\n    template <typename ScalarNumber = double>\n    class LimiterParameters : public dealii::ParameterAcceptor\n    {\n    public:\n      LimiterParameters(const std::string &subsection = \"/Limiter\")\n          : ParameterAcceptor(subsection)\n      {\n        iterations_ = 2;\n        add_parameter(\n            \"iterations\", iterations_, \"Number of limiter iterations\");\n\n        relaxation_factor_ = ScalarNumber(1.);\n        add_parameter(\"relaxation factor\",\n                      relaxation_factor_,\n                      \"Factor for scaling the relaxation window with r_i = \"\n                      \"factor * (m_i/|Omega|)^(1.5/d).\");\n      }\n\n      ACCESSOR_READ_ONLY(iterations);\n      ACCESSOR_READ_ONLY(relaxation_factor);\n\n    private:\n      unsigned int iterations_;\n      ScalarNumber relaxation_factor_;\n    };\n\n\n    /**\n     * The convex limiter.\n     *\n     * @ingroup ScalarConservationEquations\n     */\n    template <int dim, typename Number = double>\n    class Limiter\n    {\n    public:\n      /**\n       * @name Typedefs and constexpr constants\n       */\n      //@{\n\n      using View = HyperbolicSystemView<dim, Number>;\n\n      using ScalarNumber = typename View::ScalarNumber;\n\n      static constexpr auto problem_dimension = View::problem_dimension;\n\n      using state_type = typename View::state_type;\n\n      using flux_contribution_type = typename View::flux_contribution_type;\n\n      using precomputed_type = typename View::precomputed_type;\n\n      using PrecomputedVector = typename View::PrecomputedVector;\n\n      using Parameters = LimiterParameters<ScalarNumber>;\n\n      //@}\n      /**\n       * @name Computation and manipulation of bounds\n       */\n      //\n      //@{\n\n      /**\n       * The number of stored entries in the bounds array.\n       */\n      static constexpr unsigned int n_bounds = 2;\n\n      /**\n       * Array type used to store accumulated bounds.\n       */\n      using Bounds = std::array<Number, n_bounds>;\n\n      /**\n       * Constructor taking a HyperbolicSystem instance as argument\n       */\n      Limiter(const HyperbolicSystem &hyperbolic_system,\n              const Parameters &parameters,\n              const PrecomputedVector &precomputed_values)\n          : hyperbolic_system(hyperbolic_system)\n          , parameters(parameters)\n          , precomputed_values(precomputed_values)\n      {\n      }\n\n      /**\n       * Given a state @p U_i and an index @p i return \"strict\" bounds,\n       * i.e., a minimal convex set containing the state.\n       */\n      Bounds projection_bounds_from_state(const unsigned int i,\n                                          const state_type &U_i) const;\n\n      /**\n       * Given two bounds bounds_left, bounds_right, this function computes\n       * a larger, combined set of bounds that this is a (convex) superset\n       * of the two.\n       */\n      Bounds combine_bounds(const Bounds &bounds_left,\n                            const Bounds &bounds_right) const;\n\n      /**\n       * This function applies a relaxation to a given a (strict) bound @p\n       * bounds using a non dimensionalized measure @p hd (that should\n       * scale as $h^d$, where $h$ is the local mesh size).\n       */\n      Bounds fully_relax_bounds(const Bounds &bounds, const Number &hd) const;\n\n      //@}\n      /**\n       * @name Stencil-based computation of bounds\n       *\n       * Intended usage:\n       * ```\n       * Limiter<dim, Number> limiter;\n       * for (unsigned int i = n_internal; i < n_owned; ++i) {\n       *   // ...\n       *   limiter.reset(i, U_i, flux_i);\n       *   for (unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {\n       *     // ...\n       *     limiter.accumulate(js, U_j, flux_j, scaled_c_ij, affine_shift);\n       *   }\n       *   limiter.bounds(hd_i);\n       * }\n       * ```\n       */\n      //@{\n\n      /**\n       * Reset temporary storage\n       */\n      void reset(const unsigned int i,\n                 const state_type &U_i,\n                 const flux_contribution_type &flux_i);\n\n      /**\n       * When looping over the sparsity row, add the contribution associated\n       * with the neighboring state U_j.\n       */\n      void accumulate(const unsigned int *js,\n                      const state_type &U_j,\n                      const flux_contribution_type &flux_j,\n                      const dealii::Tensor<1, dim, Number> &scaled_c_ij,\n                      const state_type &affine_shift);\n\n      /**\n       * Return the computed bounds (with relaxation applied).\n       */\n      Bounds bounds(const Number hd_i) const;\n\n      //*}\n      /** @name Convex limiter */\n      //@{\n\n      /**\n       * Given a state \\f$\\mathbf U\\f$ and an update \\f$\\mathbf P\\f$ this\n       * function computes and returns the maximal coefficient \\f$t\\f$,\n       * obeying \\f$t_{\\text{min}} < t < t_{\\text{max}}\\f$, such that the\n       * selected local minimum principles are obeyed.\n       */\n      std::tuple<Number, bool> limit(const Bounds &bounds,\n                                     const state_type &U,\n                                     const state_type &P,\n                                     const Number t_min = Number(0.),\n                                     const Number t_max = Number(1.)) const;\n\n    private:\n      //@}\n      /** @name Arguments and internal fields */\n      //@{\n\n      const HyperbolicSystem &hyperbolic_system;\n      const Parameters &parameters;\n      const PrecomputedVector &precomputed_values;\n\n      state_type U_i;\n      flux_contribution_type flux_i;\n\n      Bounds bounds_;\n\n      Number u_relaxation_numerator;\n      Number u_relaxation_denominator;\n      //@}\n    };\n\n\n    /*\n     * -------------------------------------------------------------------------\n     * Inline definitions\n     * -------------------------------------------------------------------------\n     */\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    Limiter<dim, Number>::projection_bounds_from_state(\n        const unsigned int /*i*/, const state_type &U_i) const -> Bounds\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n      const auto u_i = view.state(U_i);\n      return {/*u_min*/ u_i, /*u_max*/ u_i};\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto Limiter<dim, Number>::combine_bounds(\n        const Bounds &bounds_left, const Bounds &bounds_right) const -> Bounds\n    {\n      const auto &[u_min_l, u_max_l] = bounds_left;\n      const auto &[u_min_r, u_max_r] = bounds_right;\n\n      return {std::min(u_min_l, u_min_r), std::max(u_max_l, u_max_r)};\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    Limiter<dim, Number>::fully_relax_bounds(const Bounds &bounds,\n                                             const Number &hd) const -> Bounds\n    {\n      auto relaxed_bounds = bounds;\n      auto &[u_min, u_max] = relaxed_bounds;\n\n      /* Use r = factor * (m_i / |Omega|) ^ (1.5 / d): */\n\n      Number r = std::sqrt(hd);                              // in 3D: ^ 3/6\n      if constexpr (dim == 2)                                //\n        r = dealii::Utilities::fixed_power<3>(std::sqrt(r)); // in 2D: ^ 3/4\n      else if constexpr (dim == 1)                           //\n        r = dealii::Utilities::fixed_power<3>(r);            // in 1D: ^ 3/2\n      r *= parameters.relaxation_factor();\n\n      u_min = std::min((Number(1.) - r) * u_min, (Number(1.) + r) * u_min);\n      u_max = std::max((Number(1.) + r) * u_max, (Number(1.) - r) * u_max);\n\n      return relaxed_bounds;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline void\n    Limiter<dim, Number>::reset(const unsigned int /*i*/,\n                                const state_type &new_U_i,\n                                const flux_contribution_type &new_flux_i)\n    {\n      U_i = new_U_i;\n      flux_i = new_flux_i;\n\n      /* Bounds: */\n\n      auto &[u_min, u_max] = bounds_;\n\n      u_min = Number(std::numeric_limits<ScalarNumber>::max());\n      u_max = Number(std::numeric_limits<ScalarNumber>::lowest());\n\n      /* Relaxation: */\n\n      u_relaxation_numerator = Number(0.);\n      u_relaxation_denominator = Number(0.);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline void Limiter<dim, Number>::accumulate(\n        const unsigned int * /*js*/,\n        const state_type &U_j,\n        const flux_contribution_type &flux_j,\n        const dealii::Tensor<1, dim, Number> &scaled_c_ij,\n        const state_type &affine_shift)\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      /* Bounds: */\n      auto &[u_min, u_max] = bounds_;\n\n      const auto u_i = view.state(U_i);\n      const auto u_j = view.state(U_j);\n\n      const auto U_ij_bar =\n          ScalarNumber(0.5) * (U_i + U_j) -\n          ScalarNumber(0.5) * contract(add(flux_j, -flux_i), scaled_c_ij) +\n          affine_shift;\n\n      const auto u_ij_bar = view.state(U_ij_bar);\n\n      /* Bounds: */\n\n      u_min = std::min(u_min, u_ij_bar);\n      u_max = std::max(u_max, u_ij_bar);\n\n      /* Relaxation: */\n\n      /* Use a uniform weight. */\n      const auto beta_ij = Number(1.);\n      u_relaxation_numerator += beta_ij * (u_i + u_j);\n      u_relaxation_denominator += std::abs(beta_ij);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    Limiter<dim, Number>::bounds(const Number hd_i) const -> Bounds\n    {\n      const auto &[u_min, u_max] = bounds_;\n\n      auto relaxed_bounds = fully_relax_bounds(bounds_, hd_i);\n      auto &[u_min_relaxed, u_max_relaxed] = relaxed_bounds;\n\n      /* Apply a stricter window: */\n\n      constexpr ScalarNumber eps = std::numeric_limits<ScalarNumber>::epsilon();\n\n      const Number u_relaxation =\n          ScalarNumber(2. * parameters.relaxation_factor()) *\n          std::abs(u_relaxation_numerator) /\n          (std::abs(u_relaxation_denominator) + Number(eps));\n\n      u_min_relaxed = std::max(u_min_relaxed, u_min - u_relaxation);\n      u_max_relaxed = std::min(u_max_relaxed, u_max + u_relaxation);\n\n      return relaxed_bounds;\n    }\n  } // namespace ScalarConservation\n} // namespace ryujin\n"
  },
  {
    "path": "source/scalar_conservation/limiter.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include \"limiter.h\"\n\nnamespace ryujin\n{\n  namespace ScalarConservation\n  {\n    template <int dim, typename Number>\n    std::tuple<Number, bool>\n    Limiter<dim, Number>::limit(const Bounds &bounds,\n                                const state_type &U,\n                                const state_type &P,\n                                const Number t_min /* = Number(0.) */,\n                                const Number t_max /* = Number(1.) */) const\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      bool success = true;\n      Number t_r = t_max;\n\n      constexpr ScalarNumber eps = std::numeric_limits<ScalarNumber>::epsilon();\n      const ScalarNumber relax = ScalarNumber(1. + 10000. * eps);\n\n      const auto &u_U = view.state(U);\n      const auto &u_P = view.state(P);\n\n      const auto &u_min = std::get<0>(bounds);\n      const auto &u_max = std::get<1>(bounds);\n\n      /*\n       * Verify that u_U is within bounds. This property might be\n       * violated for relative CFL numbers larger than 1.\n       *\n       * u_min, u_U, u_max might be negative, thus relax in both directions.\n       */\n      const auto test_max = std::max(\n          Number(0.), std::min(u_U - relax * u_max, relax * u_U - u_max));\n      const auto test_min = std::max(\n          Number(0.), std::min(u_min - relax * u_U, relax * u_min - u_U));\n      if (!(test_max == Number(0.) && test_min == Number(0.))) {\n#ifdef DEBUG_OUTPUT\n        std::cout << std::fixed << std::setprecision(16);\n        std::cout << \"Bounds violation: low-order state (critical)!\"\n                  << \"\\n\\t\\tu min:         \" << u_min\n                  << \"\\n\\t\\tu min (delta): \" << negative_part(u_U - u_min)\n                  << \"\\n\\t\\tu:             \" << u_U\n                  << \"\\n\\t\\tu max (delta): \" << positive_part(u_U - u_max)\n                  << \"\\n\\t\\tu max:         \" << u_max << \"\\n\"\n                  << std::endl;\n#endif\n        success = false;\n      }\n\n      const auto regularization =\n          Number(100. * std::numeric_limits<ScalarNumber>::min());\n\n      const Number denominator =\n          ScalarNumber(1.) /\n          std::max(regularization, std::abs(u_P) + eps * u_max);\n\n      t_r = dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(\n          u_max,\n          u_U + t_r * u_P,\n          /*\n           * u_P is positive.\n           *\n           * Note: Do not take an absolute value here. If we are out of\n           * bounds we have to ensure that t_r is set to t_min.\n           */\n          (u_max - u_U) * denominator,\n          t_r);\n\n      t_r = dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(\n          u_U + t_r * u_P,\n          u_min,\n          /*\n           * u_P is negative.\n           *\n           * Note: Do not take an absolute value here. If we are out of\n           * bounds we have to ensure that t_r is set to t_min.\n           */\n          (u_U - u_min) * denominator,\n          t_r);\n\n      /*\n       * Ensure that t_min <= t <= t_max. This might not be the case if\n       * u_U is outside the interval [u_min, u_max]. Furthermore,\n       * the quotient we take above is prone to numerical cancellation in\n       * particular in the second pass of the limiter when u_P might be\n       * small.\n       */\n      t_r = std::min(t_r, t_max);\n      t_r = std::max(t_r, t_min);\n\n#ifdef DEBUG_EXPENSIVE_BOUNDS_CHECK\n      /*\n       * Verify that the new state is within bounds:\n       *\n       * u_min, u_U, u_max might be negative, thus relax in both directions.\n       */\n      const auto u_new = view.state(U + t_r * P);\n      const auto test_new_max = std::max(\n          Number(0.), std::min(u_new - relax * u_max, relax * u_new - u_max));\n      const auto test_new_min = std::max(\n          Number(0.), std::min(u_min - relax * u_new, relax * u_min - u_new));\n      if (!(test_new_max == Number(0.) && test_new_min == Number(0.))) {\n#ifdef DEBUG_OUTPUT\n        std::cout << std::fixed << std::setprecision(16);\n        std::cout << \"Bounds violation: high-order state!\"\n                  << \"\\n\\t\\tu min:         \" << u_min\n                  << \"\\n\\t\\tu min (delta): \" << negative_part(u_new - u_min)\n                  << \"\\n\\t\\tu:             \" << u_new\n                  << \"\\n\\t\\tu max (delta): \" << positive_part(u_new - u_max)\n                  << \"\\n\\t\\tu max:         \" << u_max << \"\\n\"\n                  << std::endl;\n#endif\n        success = false;\n      }\n#endif\n\n      return {t_r, success};\n    }\n\n  } // namespace ScalarConservation\n} // namespace ryujin\n"
  },
  {
    "path": "source/scalar_conservation/riemann_solver.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2023 by the ryujin authors\n//\n\n#include \"riemann_solver.template.h\"\n\n#include <deal.II/base/vectorization.h>\n\nnamespace ryujin\n{\n  namespace ScalarConservation\n  {\n    /* instantiations */\n\n    template class RiemannSolver<1, NUMBER>;\n    template class RiemannSolver<2, NUMBER>;\n    template class RiemannSolver<3, NUMBER>;\n\n    template class RiemannSolver<1, dealii::VectorizedArray<NUMBER>>;\n    template class RiemannSolver<2, dealii::VectorizedArray<NUMBER>>;\n    template class RiemannSolver<3, dealii::VectorizedArray<NUMBER>>;\n\n  } // namespace ScalarConservation\n} // namespace ryujin\n"
  },
  {
    "path": "source/scalar_conservation/riemann_solver.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"hyperbolic_system.h\"\n\n#include <simd.h>\n\n#include <deal.II/base/point.h>\n#include <deal.II/base/tensor.h>\n\nnamespace ryujin\n{\n  namespace ScalarConservation\n  {\n    template <typename ScalarNumber = double>\n    class RiemannSolverParameters : public dealii::ParameterAcceptor\n    {\n    public:\n      RiemannSolverParameters(const std::string &subsection = \"/RiemannSolver\")\n          : ParameterAcceptor(subsection)\n      {\n        use_greedy_wavespeed_ = false;\n        add_parameter(\"use greedy wavespeed\",\n                      use_greedy_wavespeed_,\n                      \"Use a greedy wavespeed estimate instead of a guaranteed \"\n                      \"upper bound \"\n                      \"on the maximal wavespeed (for convex fluxes).\");\n\n        use_averaged_entropy_ = false;\n        add_parameter(\"use averaged entropy\",\n                      use_averaged_entropy_,\n                      \"In addition to the wavespeed estimate based on the Roe \"\n                      \"average and \"\n                      \"flux gradients of the left and right state also enforce \"\n                      \"an entropy \"\n                      \"inequality on the averaged Krŭzkov entropy.\");\n\n        random_entropies_ = 0;\n        add_parameter(\n            \"random entropies\",\n            random_entropies_,\n            \"In addition to the wavespeed estimate based on the Roe average \"\n            \"and \"\n            \"flux gradients of the left and right state also enforce an \"\n            \"entropy \"\n            \"inequality on the prescribed number of random Krŭzkov entropies.\");\n      }\n\n      ACCESSOR_READ_ONLY(use_greedy_wavespeed);\n      ACCESSOR_READ_ONLY(use_averaged_entropy);\n      ACCESSOR_READ_ONLY(random_entropies);\n\n    private:\n      bool use_greedy_wavespeed_;\n      bool use_averaged_entropy_;\n      unsigned int random_entropies_;\n    };\n\n\n    /**\n     * A fast estimate for a sufficient maximal wavespeed of the 1D Riemann\n     * problem. The wavespeed estimate is based on a guaranteed upper bound\n     * on the maximal wavespeed for convex fluxes, see Example 79.17 on\n     * page 333 of @cite GuermondErn2021. As well as an augmented \"Roe\n     * average\" based on an entropy inequality of a suitable Krŭzkov\n     * entropy, see @cite ryujin-2023-5 Section 4.\n     *\n     * @ingroup ScalarConservationEquations\n     */\n    template <int dim, typename Number = double>\n    class RiemannSolver\n    {\n    public:\n      /**\n       * @name Typedefs and constexpr constants\n       */\n      //@{\n\n      using View = HyperbolicSystemView<dim, Number>;\n\n      using ScalarNumber = typename View::ScalarNumber;\n\n      static constexpr auto problem_dimension = View::problem_dimension;\n\n      using state_type = typename View::state_type;\n\n      using precomputed_type = typename View::precomputed_type;\n\n      using PrecomputedVector = typename View::PrecomputedVector;\n\n      using Parameters = RiemannSolverParameters<ScalarNumber>;\n\n      //@}\n\n      /**\n       * @name Compute wavespeed estimates\n       */\n      //@{\n\n      /**\n       * Constructor taking a HyperbolicSystem instance as argument\n       */\n      RiemannSolver(const HyperbolicSystem &hyperbolic_system,\n                    const Parameters &parameters,\n                    const PrecomputedVector &precomputed_values)\n          : hyperbolic_system(hyperbolic_system)\n          , parameters(parameters)\n          , precomputed_values(precomputed_values)\n      {\n      }\n\n      /**\n       * For two states @p u_i, @p u_j, precomputed values @p prec_i,\n       * @p prec_j, and a (normalized) \"direction\" n_ij\n       * compute an upper bound estimate for the wavespeed.\n       */\n      Number compute(const Number &u_i,\n                     const Number &u_j,\n                     const precomputed_type &prec_i,\n                     const precomputed_type &prec_j,\n                     const dealii::Tensor<1, dim, Number> &n_ij) const;\n\n      /**\n       * For two given states U_i a U_j and a (normalized) \"direction\" n_ij\n       * compute an estimate for an upper bound of lambda.\n       */\n      Number compute(const state_type &U_i,\n                     const state_type &U_j,\n                     const unsigned int i,\n                     const unsigned int *js,\n                     const dealii::Tensor<1, dim, Number> &n_ij) const;\n\n    private:\n      const HyperbolicSystem &hyperbolic_system;\n      const Parameters &parameters;\n      const PrecomputedVector &precomputed_values;\n      //@}\n    };\n  } // namespace ScalarConservation\n} // namespace ryujin\n"
  },
  {
    "path": "source/scalar_conservation/riemann_solver.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2024 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"riemann_solver.h\"\n\n#include <simd.h>\n\n#include <random>\n\n// #define DEBUG_RIEMANN_SOLVER\n\nnamespace ryujin\n{\n  namespace ScalarConservation\n  {\n    template <int dim, typename Number>\n    Number RiemannSolver<dim, Number>::compute(\n        const Number &u_i,\n        const Number &u_j,\n        const precomputed_type &prec_i,\n        const precomputed_type &prec_j,\n        const dealii::Tensor<1, dim, Number> &n_ij) const\n    {\n      const auto &view = hyperbolic_system.view<dim, Number>();\n\n      /* Project all fluxes to 1D: */\n      const Number f_i = view.construct_flux_tensor(prec_i) * n_ij;\n      const Number f_j = view.construct_flux_tensor(prec_j) * n_ij;\n      const Number df_i = view.construct_flux_gradient_tensor(prec_i) * n_ij;\n      const Number df_j = view.construct_flux_gradient_tensor(prec_j) * n_ij;\n\n      const auto h2 = Number(2. * view.derivative_approximation_delta());\n\n#ifdef DEBUG_RIEMANN_SOLVER\n      std::cout << \"\\nu_i  = \" << u_i << std::endl;\n      std::cout << \"u_j  = \" << u_j << std::endl;\n      std::cout << \"f_i  = \" << f_i << std::endl;\n      std::cout << \"f_j  = \" << f_j << std::endl;\n      std::cout << \"df_i = \" << df_i << std::endl;\n      std::cout << \"df_j = \" << df_j << std::endl;\n#endif\n\n      /*\n       * The Roe average with a regularization based on $h$ which is the\n       * step size used for the central difference approximation of f'(u).\n       *\n       * The regularization max(|u_i - u_j|, 2 * h) ensures that the\n       * quotient approximates the derivative f'( (u_i + u_j)/2 ) to the\n       * same precision that we use to compute f'(u_i) and f'(u_j) in the\n       * FunctionParser (via a central difference approximation).\n       *\n       * This implies that in contrast to the actual limit of the\n       * difference quotient we will approach 0 as |u_j - u_i| goes to\n       * zero. We fix this by taking the maximum with our approximation of\n       * f'(u_i) and f'(u_j) further down below.\n       */\n\n      auto lambda_max = std::abs(f_i - f_j) / std::max(std::abs(u_i - u_j), h2);\n#ifdef DEBUG_RIEMANN_SOLVER\n      std::cout << \"   Roe average       = \" << lambda_max << std::endl;\n#endif\n\n      constexpr auto gte = dealii::SIMDComparison::greater_than_or_equal;\n\n      if (parameters.use_greedy_wavespeed()) {\n        /*\n         * In case of a greedy estimate we make sure that we always use the\n         * Roe average and only fall back to the derivative approximation\n         * when u_i and u_j are close to each other within 2h:\n         */\n        lambda_max = dealii::compare_and_apply_mask<gte>(\n            std::abs(u_i - u_j),\n            h2,\n            lambda_max,\n            /* Approximate derivative in centerpoint: */\n            std::abs(ScalarNumber(0.5) * (df_i + df_j)));\n#ifdef DEBUG_RIEMANN_SOLVER\n        std::cout << \"   interpolated      = \"\n                  << std::abs(ScalarNumber(0.5) * (df_i + df_j)) << std::endl;\n#endif\n\n      } else {\n        /*\n         * Always take the maximum with |f'(u_i)| and |f'(u_j)|.\n         *\n         * For convex fluxes this implies that lambda_max is indeed the\n         * maximal wavespeed of the system. See Example 79.17 in reference\n         * @cite ErnGuermond2021.\n         */\n        lambda_max = std::max(lambda_max, std::abs(df_i));\n        lambda_max = std::max(lambda_max, std::abs(df_j));\n#ifdef DEBUG_RIEMANN_SOLVER\n        std::cout << \"   left  derivative  = \" << std::abs(df_i) << std::endl;\n        std::cout << \"   right derivative  = \" << std::abs(df_j) << std::endl;\n#endif\n      }\n\n      /*\n       * Thread-local helper lambda to generate a random number in [0,1]:\n       */\n\n      thread_local static const auto draw = []() {\n        static std::random_device random_device;\n        static auto generator = std::default_random_engine(random_device());\n        static std::uniform_real_distribution<ScalarNumber> dist(0., 1.);\n\n        if constexpr (std::is_same_v<ScalarNumber, Number>) {\n          /*\n           * Scalar quantity:\n           */\n          return dist(generator);\n\n        } else {\n          /*\n           * Populate a vectorized array:\n           */\n          Number result;\n          for (unsigned int s = 0; s < Number::size(); ++s)\n            result[s] = dist(generator);\n          return result;\n        }\n      };\n\n      /*\n       * Helper functions for enforcing entropy inequalities:\n       */\n\n      const auto enforce_entropy = [&](const Number &k) {\n        const Number f_k = view.flux_function(k) * n_ij;\n\n#ifdef DEBUG_RIEMANN_SOLVER\n        std::cout << \"k    = \" << k << std::endl;\n        std::cout << \"f_k  = \" << f_k << std::endl;\n#endif\n\n        const Number eta_i = view.kruzkov_entropy(k, u_i);\n        const Number q_i =\n            view.kruzkov_entropy_derivative(k, u_i) * (f_i - f_k);\n\n        const Number eta_j = view.kruzkov_entropy(k, u_j);\n        const Number q_j =\n            view.kruzkov_entropy_derivative(k, u_j) * (f_j - f_k);\n\n        const Number a = u_i + u_j - ScalarNumber(2.) * k;\n        const Number b = f_j - f_i;\n        const Number c = eta_i + eta_j;\n        const Number d = q_j - q_i;\n\n        /*\n         * FIXME: Ordinarily, lambda_left and lambda_right would be\n         * computed without taking the absolute value of the numerator.\n         * (The denominator is - in the absence of rounding errors - always\n         * nonnegative. The numerator has a sign.)\n         * But empirically it turns out that taking the absolute value and\n         * letting both estimates participate in the maximal wavespeed\n         * estimate helps a lot.\n         */\n        const Number lambda_left = std::abs(d + b) / (std::abs(c + a) + h2);\n        const Number lambda_right = std::abs(d - b) / (std::abs(c - a) + h2);\n\n#ifdef DEBUG_RIEMANN_SOLVER\n        std::cout << \"   left  wavespeed   = \" << lambda_left << std::endl;\n        std::cout << \"   right wavespeed   = \" << lambda_right << std::endl;\n#endif\n        lambda_max = std::max(lambda_max, lambda_left);\n        lambda_max = std::max(lambda_max, lambda_right);\n      };\n\n\n      if (parameters.use_averaged_entropy()) {\n        const Number k = ScalarNumber(0.5) * (u_i + u_j);\n        enforce_entropy(k);\n      }\n\n      const unsigned int n_entropies = parameters.random_entropies();\n      for (unsigned int i = 0; i < n_entropies; ++i) {\n        const Number factor = draw();\n        const Number k = factor * u_i + (Number(1.) - factor) * u_j;\n        enforce_entropy(k);\n      }\n\n#ifdef DEBUG_RIEMANN_SOLVER\n      std::cout << \"-> lambda_max        = \" << lambda_max << std::endl;\n#endif\n      return lambda_max;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number RiemannSolver<dim, Number>::compute(\n        const state_type &U_i,\n        const state_type &U_j,\n        const unsigned int i,\n        const unsigned int *js,\n        const dealii::Tensor<1, dim, Number> &n_ij) const\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      using pst = typename View::precomputed_type;\n\n      const auto u_i = view.state(U_i);\n      const auto u_j = view.state(U_j);\n\n      const auto &pv = precomputed_values;\n      const auto prec_i = pv.template read_tensor<Number, pst>(i);\n      const auto prec_j = pv.template read_tensor<Number, pst>(js);\n\n      return compute(u_i, u_j, prec_i, prec_j, n_ij);\n    }\n\n  } // namespace ScalarConservation\n} // namespace ryujin\n"
  },
  {
    "path": "source/scope.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include \"instrumentation.h\"\n#include <compile_time_options.h>\n\n#include <deal.II/base/timer.h>\n\n#include <map>\n#include <string>\n\n#ifdef DEBUG_OUTPUT\n#include <iostream>\n#endif\n\nnamespace ryujin\n{\n  /**\n   * A RAII scope for deal.II timer objects and likwid instrumentation.\n   *\n   * The constructor of the class starts a timer with the specified name.\n   * If ryujin is configured with likwid then likwid instrumentation will\n   * also be started with the given section name. The destructor of the\n   * class stops the timer again and also stops likwid instrumentation.\n   *\n   * @note This class does not perform MPI synchronization in contrast to\n   * the deal.II counterpart.\n   *\n   * @ingroup Miscellaneous\n   */\n  class Scope\n  {\n  public:\n    /**\n     * Constructor. Starts a timer for the selected @p section.\n     */\n    Scope(std::map<std::string, dealii::Timer> &computing_timer,\n          const std::string &section)\n        : computing_timer_(computing_timer)\n        , section_(section)\n    {\n      LIKWID_MARKER_START(section_.c_str());\n      computing_timer_[section_].start();\n#ifdef DEBUG_OUTPUT\n      std::cout << \"{scoped timer} \\\"\" << section_ << \"\\\" started\" << std::endl;\n#endif\n    }\n\n    /**\n     * Destructor. Stops the timer.\n     */\n    ~Scope()\n    {\n#ifdef DEBUG_OUTPUT\n      std::cout << \"{scoped timer} \\\"\" << section_ << \"\\\" stopped\" << std::endl;\n#endif\n      computing_timer_[section_].stop();\n      LIKWID_MARKER_STOP(section_.c_str());\n    }\n\n  private:\n    std::map<std::string, dealii::Timer> &computing_timer_;\n    const std::string section_;\n  };\n} // namespace ryujin\n"
  },
  {
    "path": "source/scratch_data.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"discretization.h\"\n\n#include <deal.II/base/quadrature_lib.h>\n#include <deal.II/hp/fe_values.h>\n#include <deal.II/lac/full_matrix.h>\n\nnamespace ryujin\n{\n\n  /**\n   * Internal scratch data for thread parallelized assembly. See the\n   * deal.II Workstream documentation for details.\n   *\n   * @ingroup Mesh\n   */\n  template <int dim>\n  class AssemblyScratchData\n  {\n  public:\n    AssemblyScratchData(const AssemblyScratchData<dim> &assembly_scratch_data)\n        : AssemblyScratchData(assembly_scratch_data.discretization_)\n    {\n    }\n\n\n    AssemblyScratchData(const Discretization<dim> &discretization)\n        : discretization_(discretization)\n        , hp_fe_values_(discretization_.mapping(),\n                        discretization_.finite_element(),\n                        discretization_.quadrature(),\n                        dealii::update_values | dealii::update_gradients |\n                            dealii::update_quadrature_points |\n                            dealii::update_JxW_values)\n        , hp_fe_face_values_(\n              discretization_.mapping(),\n              discretization_.finite_element(),\n              discretization_.face_quadrature(),\n              dealii::update_values | dealii::update_quadrature_points |\n                  dealii::update_JxW_values | dealii::update_normal_vectors)\n        , hp_fe_face_values_nodal_(discretization_.mapping(),\n                                   discretization_.finite_element(),\n                                   discretization_.face_nodal_quadrature(),\n                                   dealii::update_values |\n                                       dealii::update_quadrature_points)\n        , hp_fe_neighbor_face_values_(discretization_.mapping(),\n                                      discretization_.finite_element(),\n                                      discretization_.face_quadrature(),\n                                      dealii::update_values)\n        , hp_fe_neighbor_face_values_nodal_(\n              discretization_.mapping(),\n              discretization_.finite_element(),\n              discretization_.face_nodal_quadrature(),\n              dealii::update_values)\n    {\n    }\n\n    const Discretization<dim> &discretization_;\n\n    dealii::hp::FEValues<dim> hp_fe_values_;\n    dealii::hp::FEFaceValues<dim> hp_fe_face_values_;\n    dealii::hp::FEFaceValues<dim> hp_fe_face_values_nodal_;\n    dealii::hp::FEFaceValues<dim> hp_fe_neighbor_face_values_;\n    dealii::hp::FEFaceValues<dim> hp_fe_neighbor_face_values_nodal_;\n  };\n\n  /**\n   * Internal copy data for thread parallelized assembly. See the deal.II\n   * Workstream documentation for details.\n   */\n  template <int dim, typename Number = double>\n  class AssemblyCopyData\n  {\n  public:\n    bool is_locally_owned_;\n    std::vector<dealii::types::global_dof_index> local_dof_indices_;\n    dealii::FullMatrix<Number> cell_mass_matrix_;\n    dealii::FullMatrix<Number> cell_mass_matrix_inverse_;\n    dealii::FullMatrix<Number> cell_betaij_matrix_;\n    std::array<dealii::FullMatrix<Number>, dim> cell_cij_matrix_;\n    Number cell_measure_;\n\n    static constexpr unsigned int n_faces = 2 * dim;\n    std::array<std::vector<dealii::types::global_dof_index>, n_faces>\n        neighbor_local_dof_indices_;\n    std::array<std::array<dealii::FullMatrix<Number>, dim>, n_faces>\n        interface_cij_matrix_;\n    std::array<dealii::FullMatrix<Number>, n_faces> interface_incidence_matrix_;\n  };\n\n} // namespace ryujin\n"
  },
  {
    "path": "source/selected_components_extractor.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2024 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"state_vector.h\"\n\nnamespace ryujin\n{\n  template <typename Description, int dim, typename Number>\n  struct SelectedComponentsExtractor {\n    using HyperbolicSystem = typename Description::HyperbolicSystem;\n    using ParabolicSystem = typename Description::ParabolicSystem;\n\n    using View =\n        typename Description::template HyperbolicSystemView<dim, Number>;\n\n    using StateVector = typename View::StateVector;\n    using InitialPrecomputedVector = typename View::InitialPrecomputedVector;\n\n    using ScalarVector = Vectors::ScalarVector<Number>;\n    using ScalarHostVector = Vectors::ScalarHostVector<Number>;\n\n    SelectedComponentsExtractor() = delete;\n\n    static void check(const std::vector<std::string> &parabolic_component_names,\n                      const std::vector<std::string> &additional_names,\n                      const std::vector<std::string> &selected)\n    {\n      const auto search = [&](const auto entry, const auto &names) {\n        const auto pos = std::find(std::begin(names), std::end(names), entry);\n        return pos != std::end(names);\n      };\n\n      for (const auto &entry : selected) {\n        const auto found = search(entry, View::component_names) ||\n                           search(entry, View::primitive_component_names) ||\n                           search(entry, parabolic_component_names) ||\n                           search(entry, View::precomputed_names) ||\n                           search(entry, View::initial_precomputed_names) ||\n                           search(entry, additional_names);\n        AssertThrow(found,\n                    dealii::ExcMessage(\n                        \"Invalid component name: \\\"\" + entry +\n                        \"\\\" is not a valid conserved, primitive, parabolic, \"\n                        \"precomputed, or initial component name.\"));\n      }\n    }\n\n    static std::vector<ScalarHostVector>\n    extract(const OfflineData<dim, Number> &offline_data,\n            const HyperbolicSystem &hyperbolic_system,\n            const ParabolicSystem &parabolic_system,\n            const StateVector &state_vector,\n            const InitialPrecomputedVector &initial_precomputed,\n            const std::vector<std::string> &additional_names,\n            const std::vector<std::reference_wrapper<const ScalarVector>>\n                &additional_vectors,\n            const std::vector<std::string> &selected)\n    {\n      /*\n       * Match the selected_components strings against conserved,\n       * primitive, and initial component names and record an index pair\n       * matching return vector position and component index:\n       */\n\n      std::vector<std::tuple<std::size_t, std::size_t>> conserved_indices;\n      std::vector<std::tuple<std::size_t, std::size_t>> primitive_indices;\n      std::vector<std::tuple<std::size_t, std::size_t>> precomputed_indices;\n      std::vector<std::tuple<std::size_t, std::size_t>> parabolic_indices;\n      std::vector<std::tuple<std::size_t, std::size_t>> initial_indices;\n      std::vector<std::tuple<std::size_t, std::size_t>> additional_indices;\n\n      for (std::size_t i = 0; const auto &entry : selected) {\n        const auto search = [&](const auto &names, auto &indices) {\n          const auto pos = std::find(std::begin(names), std::end(names), entry);\n          if (pos != std::end(names)) {\n            const auto index = std::distance(std::begin(names), pos);\n            indices.push_back({i++, index});\n            return true;\n          }\n          return false;\n        };\n\n        if (search(View::component_names, conserved_indices))\n          ;\n        else if (search(View::primitive_component_names, primitive_indices))\n          ;\n        else if (search(View::precomputed_names, precomputed_indices))\n          ;\n        else if (search(parabolic_system.parabolic_component_names(),\n                        parabolic_indices))\n          ;\n        else if (search(View::initial_precomputed_names, initial_indices))\n          ;\n        else if (search(additional_names, additional_indices))\n          ;\n        else\n          AssertThrow(false, dealii::ExcInternalError());\n      }\n\n      std::vector<ScalarHostVector> extracted_components(selected.size());\n      const auto &scalar_partitioner = offline_data.scalar_partitioner();\n      for (auto &it : extracted_components)\n        it.reinit(scalar_partitioner);\n\n      for (const auto &[i, k] : conserved_indices) {\n        const auto &U = std::get<0>(state_vector);\n        U.extract_component(extracted_components[i], k);\n      }\n\n      if (!primitive_indices.empty()) {\n        const auto &U = std::get<0>(state_vector);\n        const unsigned int n_owned = scalar_partitioner->locally_owned_size();\n        const auto view = hyperbolic_system.template view<dim, Number>();\n        for (unsigned int i = 0; i < n_owned; ++i) {\n          const auto U_i = U.read_tensor(i);\n          const auto PU_i = view.to_primitive_state(U_i);\n          for (const auto &[j, k] : primitive_indices)\n            extracted_components[j].local_element(i) = PU_i[k];\n        }\n      }\n\n      for (const auto &[i, k] : precomputed_indices) {\n        const auto &prec = std::get<1>(state_vector);\n        prec.extract_component(extracted_components[i], k);\n      }\n\n      for (const auto &[i, k] : parabolic_indices) {\n        const auto &parabolic = std::get<2>(state_vector);\n        extracted_components[i] = parabolic.block(k);\n      }\n\n      for (const auto &[i, k] : initial_indices) {\n        initial_precomputed.extract_component(extracted_components[i], k);\n      }\n\n      for (const auto &[i, k] : additional_indices) {\n        additional_vectors[k].get().extract_component( //\n            extracted_components[i],\n            0);\n      }\n\n      return extracted_components;\n    }\n  };\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/CMakeLists.txt",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2022 - 2024 by the ryujin authors\n##\n\nadd_library(obj_shallow_water OBJECT\n  equation_dispatch.cc\n  initial_state_library.cc\n  limiter.cc\n  riemann_solver.cc\n  )\nset_target_properties(obj_shallow_water PROPERTIES LINKER_LANGUAGE CXX)\ndeal_ii_setup_target(obj_shallow_water)\ntarget_link_libraries(obj_shallow_water obj_common ${EXTERNAL_TARGETS})\n# Propagate the current source directory with PUBLIC visibility\ntarget_include_directories(obj_shallow_water PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})\n"
  },
  {
    "path": "source/shallow_water/Makefile",
    "content": "default: all\n.PHONY: default\n\n%:\n\t@cd .. && make $@\n.PHONY: %\n"
  },
  {
    "path": "source/shallow_water/description.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2023 - 2025 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"../stub_parabolic_module.h\"\n#include \"../stub_parabolic_system.h\"\n#include \"hyperbolic_system.h\"\n#include \"indicator.h\"\n#include \"limiter.h\"\n#include \"riemann_solver.h\"\n\nnamespace ryujin\n{\n  namespace ShallowWater\n  {\n    /**\n     * A struct that contains all equation specific classes describing the\n     * chosen hyperbolic system, the indicator, the limiter and\n     * (approximate) Riemann solver.\n     *\n     * The compressible shallow water equations.\n     *\n     * The parabolic subsystem is chosen to be the identity.\n     *\n     * @ingroup ShallowWaterEquations\n     */\n    struct Description {\n      using HyperbolicSystem = ShallowWater::HyperbolicSystem;\n\n      template <int dim, typename Number = double>\n      using HyperbolicSystemView =\n          ShallowWater::HyperbolicSystemView<dim, Number>;\n\n      using ParabolicSystem = ryujin::StubParabolicSystem;\n\n      template <int dim, typename Number = double>\n      using ParabolicModule =\n          ryujin::StubParabolicModule<Description, dim, Number>;\n\n      template <int dim, typename Number = double>\n      using Indicator = ShallowWater::Indicator<dim, Number>;\n\n      template <int dim, typename Number = double>\n      using Limiter = ShallowWater::Limiter<dim, Number>;\n\n      template <int dim, typename Number = double>\n      using RiemannSolver = ShallowWater::RiemannSolver<dim, Number>;\n    };\n  } // namespace ShallowWater\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/equation_dispatch.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2024 by the ryujin authors\n//\n\n#include \"description.h\"\n\n#include <compile_time_options.h>\n#include <equation_dispatch.h>\n\nnamespace ryujin\n{\n  namespace ShallowWater\n  {\n    Dispatch<Description, NUMBER> dispatch_instance(\"shallow water\");\n  } // namespace ShallowWater\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/hyperbolic_system.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2020 - 2025 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <convenience_macros.h>\n#include <discretization.h>\n#include <loop.h>\n#include <multicomponent_vector.h>\n#include <patterns_conversion.h>\n#include <simd.h>\n#include <state_vector.h>\n\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/tensor.h>\n\n#include <array>\n\nnamespace ryujin\n{\n  namespace ShallowWater\n  {\n    template <int dim, typename Number>\n    class HyperbolicSystemView;\n\n    /**\n     * Description of a @p dim dimensional hyperbolic conservation law\n     * modeling the shallow water equations.\n     *\n     * We have a (1 + dim) dimensional state space \\f$[h, \\textbf m]\\f$, where\n     * \\f$h\\f$ denotes the water depth, abd \\f$\\textbf m\\f$ is the momentum.\n     *\n     * @ingroup ShallowWaterEquations\n     */\n    class HyperbolicSystem final : public dealii::ParameterAcceptor\n    {\n    public:\n      /**\n       * The name of the hyperbolic system as a string.\n       */\n      static inline const std::string problem_name = \"Shallow water equations\";\n\n      /**\n       * Constructor.\n       */\n      HyperbolicSystem(const std::string &subsection = \"/HyperbolicSystem\");\n\n      /**\n       * Return a view on the Hyperbolic System for a given dimension @p\n       * dim and choice of number type @p Number (which can be a scalar\n       * float, or double, as well as a VectorizedArray holding packed\n       * scalars.\n       */\n      template <int dim, typename Number>\n      auto view() const\n      {\n        return HyperbolicSystemView<dim, Number>{*this};\n      }\n\n      /**\n       * Part of step 1 of the hyperbolic update step: Compute \"precomputed\n       * values\" and fill into the state vector.\n       *\n       * @note The method does not update the ghost range of the state\n       * vector. The precomputed part has to be synchronized by explicitly\n       * calling the update ghost values function.\n       */\n      template <int dim, typename ScalarNumber>\n      void fill_precomputed_values(\n          const OfflineData<dim, ScalarNumber> &offline_data,\n          typename HyperbolicSystemView<dim, ScalarNumber>::StateVector\n              &state_vector,\n          const bool skip_constrained_dofs = true) const;\n\n    private:\n      /**\n       * @name Runtime parameters, internal fields, methods, and friends\n       */\n      //@{\n      double gravity_;\n      double manning_friction_coefficient_;\n\n      double reference_water_depth_;\n      double dry_state_relaxation_small_;\n      double dry_state_relaxation_large_;\n\n      template <int dim, typename Number>\n      friend class HyperbolicSystemView;\n      //@}\n    }; /* HyperbolicSystem */\n\n\n    /**\n     * A view of the HyperbolicSystem that makes methods available for a\n     * given dimension @p dim and choice of number type @p Number (which\n     * can be a scalar float, or double, as well as a VectorizedArray\n     * holding packed scalars.\n     *\n     * Intended usage:\n     * ```\n     * HyperbolicSystem hyperbolic_system;\n     * const auto view = hyperbolic_system.template view<dim, Number>();\n     * const auto flux_i = view.flux_contribution(...);\n     * const auto flux_j = view.flux_contribution(...);\n     * const auto flux_ij = view.flux_divergence(flux_i, flux_j, c_ij);\n     * // etc.\n     * ```\n     */\n    template <int dim, typename Number>\n    class HyperbolicSystemView\n    {\n    public:\n      /**\n       * Constructor taking a reference to the underlying\n       * HyperbolicSystem\n       */\n      HyperbolicSystemView(const HyperbolicSystem &hyperbolic_system)\n          : hyperbolic_system_(hyperbolic_system)\n      {\n      }\n\n      /**\n       * Create a modified view from the current one:\n       */\n      template <int dim2, typename Number2>\n      auto view() const\n      {\n        return HyperbolicSystemView<dim2, Number2>{hyperbolic_system_};\n      }\n\n      /**\n       * The underlying scalar number type.\n       */\n      using ScalarNumber = typename get_value_type<Number>::type;\n\n      /**\n       * @name Access to runtime parameters\n       */\n      //@{\n\n      DEAL_II_ALWAYS_INLINE inline ScalarNumber gravity() const\n      {\n        return hyperbolic_system_.gravity_;\n      }\n\n      DEAL_II_ALWAYS_INLINE inline ScalarNumber\n      manning_friction_coefficient() const\n      {\n        return hyperbolic_system_.manning_friction_coefficient_;\n      }\n\n      DEAL_II_ALWAYS_INLINE inline ScalarNumber reference_water_depth() const\n      {\n        return hyperbolic_system_.reference_water_depth_;\n      }\n\n      DEAL_II_ALWAYS_INLINE inline ScalarNumber\n      dry_state_relaxation_small() const\n      {\n        return hyperbolic_system_.dry_state_relaxation_small_;\n      }\n\n      DEAL_II_ALWAYS_INLINE inline ScalarNumber\n      dry_state_relaxation_large() const\n      {\n        return hyperbolic_system_.dry_state_relaxation_large_;\n      }\n\n      //@}\n      /**\n       * @name Internal data\n       */\n      //@{\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n    public:\n      //@}\n      /**\n       * @name Types and constexpr constants\n       */\n      //@{\n\n      /**\n       * The dimension of the state space.\n       */\n      static constexpr unsigned int problem_dimension = 1 + dim;\n\n      /**\n       * Storage type for a (conserved) state vector \\f$\\boldsymbol U\\f$.\n       */\n      using state_type = dealii::Tensor<1, problem_dimension, Number>;\n\n      /**\n       * Storage type for the flux \\f$\\mathbf{f}\\f$.\n       */\n      using flux_type =\n          dealii::Tensor<1, problem_dimension, dealii::Tensor<1, dim, Number>>;\n\n      /**\n       * The storage type used for flux contributions.\n       */\n      using flux_contribution_type = std::tuple<state_type, Number>;\n\n      /**\n       * An array holding all component names of the conserved state as a\n       * string.\n       */\n      static inline const auto component_names =\n          []() -> std::array<std::string, problem_dimension> {\n        if constexpr (dim == 1)\n          return {\"h\", \"m\"};\n        else if constexpr (dim == 2)\n          return {\"h\", \"m_1\", \"m_2\"};\n        else if constexpr (dim == 3)\n          return {\"h\", \"m_1\", \"m_2\", \"m_3\"};\n        __builtin_trap();\n      }();\n\n      /**\n       * An array holding all component names of the primitive state as a\n       * string.\n       */\n      static inline const auto primitive_component_names =\n          []() -> std::array<std::string, problem_dimension> {\n        if constexpr (dim == 1)\n          return {\"h\", \"v\"};\n        else if constexpr (dim == 2)\n          return {\"h\", \"v_1\", \"v_2\"};\n        else if constexpr (dim == 3)\n          return {\"h\", \"v_1\", \"v_2\", \"v_3\"};\n        __builtin_trap();\n      }();\n\n      /**\n       * The number of precomputed values.\n       */\n      static constexpr unsigned int n_precomputed_values = 2;\n\n      /**\n       * Array type used for precomputed values.\n       */\n      using precomputed_type = std::array<Number, n_precomputed_values>;\n\n      /**\n       * An array holding all component names of the precomputed values.\n       */\n      static inline const auto precomputed_names =\n          std::array<std::string, n_precomputed_values>{\"eta_m\", \"h_star\"};\n\n      /**\n       * The number of precomputed initial values.\n       */\n      static constexpr unsigned int n_initial_precomputed_values = 1;\n\n      /**\n       * Array type used for precomputed initial values.\n       */\n      using initial_precomputed_type =\n          std::array<Number, n_initial_precomputed_values>;\n\n      /**\n       * An array holding all component names of the precomputed values.\n       */\n      static inline const auto initial_precomputed_names =\n          std::array<std::string, n_initial_precomputed_values>{\"bathymetry\"};\n\n      /**\n       * A compound state vector.\n       */\n      using StateVector = Vectors::\n          StateVector<ScalarNumber, problem_dimension, n_precomputed_values>;\n\n      /**\n       * MulticomponentVector for storing the hyperbolic state vector:\n       */\n      using HyperbolicVector =\n          Vectors::MultiComponentVector<ScalarNumber, problem_dimension>;\n\n      /**\n       * MulticomponentVector for storing a vector of precomputed states:\n       */\n      using PrecomputedVector =\n          Vectors::MultiComponentVector<ScalarNumber, n_precomputed_values>;\n\n      /**\n       * MulticomponentVector for storing a vector of precomputed initial\n       * states:\n       */\n      using InitialPrecomputedVector =\n          Vectors::MultiComponentVector<ScalarNumber,\n                                        n_initial_precomputed_values>;\n\n      //@}\n      /**\n       * @name Computing derived physical quantities\n       */\n      //@{\n\n      /**\n       * For a given (1+dim dimensional) state vector <code>U</code>, return\n       * the water depth <code>U[0]</code>\n       */\n      static Number water_depth(const state_type &U);\n\n      /**\n       * For a given (1+dim dimensional) state vector <code>U</code>,\n       * return a regularized inverse of the water depth. This function\n       * returns 2h / (h^2+max(h, h_cutoff)^2), where h_cutoff is the\n       * reference water depth multiplied by eps.\n       */\n      Number inverse_water_depth_mollified(const state_type &U) const;\n\n      /**\n       * For a given (1+dim dimensional) state vector <code>U</code>, return\n       * the regularized water depth <code>U[0]</code> This function returns\n       * max(h, h_cutoff), where h_cutoff is the reference water depth\n       * multiplied by eps.\n       */\n      Number water_depth_sharp(const state_type &U) const;\n\n      /**\n       * For a given (1+dim dimensional) state vector <code>U</code>, return\n       * a regularized inverse of the water depth. This function returns 1 /\n       * max(h, h_cutoff), where h_cutoff is the reference water depth\n       * multiplied by eps.\n       */\n      Number inverse_water_depth_sharp(const state_type &U) const;\n\n      /**\n       * Given a water depth @ref h this function returns 0 if h is in the\n       * interval [-relaxation * h_cutoff, relaxation * h_cutoff], otherwise\n       * h is returned unmodified. Here, h_cutoff is the reference water\n       * depth multiplied by eps.\n       */\n      Number filter_dry_water_depth(const Number &h) const;\n\n      /**\n       * For a given (1+dim dimensional) state vector <code>U</code>, return\n       * the momentum vector <code>[U[1], ..., U[1+dim]]</code>.\n       */\n      static dealii::Tensor<1, dim, Number> momentum(const state_type &U);\n\n      /**\n       * For a given (1+dim dimensional) state vector <code>U</code>, compute\n       * and return the kinetic energy.\n       * \\f[\n       *   KE = 1/2 |m|^2 / h\n       * \\f]\n       */\n      Number kinetic_energy(const state_type &U) const;\n\n      /**\n       * For a given (state dimensional) state vector <code>U</code>, compute\n       * and return the hydrostatic pressure \\f$p\\f$:\n       * \\f[\n       *   p = 1/2 g h^2\n       * \\f]\n       */\n      Number pressure(const state_type &U) const;\n\n      /**\n       * For a given (1+dim dimensional) state vector <code>U</code>, compute\n       * the (physical) speed of sound:\n       * \\f[\n       *   c^2 = g * h\n       * \\f]\n       */\n      Number speed_of_sound(const state_type &U) const;\n\n      /**\n       * For a given (1+dim dimensional) state vector <code>U</code>, compute\n       * and return the entropy \\f$\\eta = 1/2 g h^2 + 1/2 |m|^2 / h\\f$.\n       */\n      Number mathematical_entropy(const state_type &U) const;\n\n      /**\n       * For a given (1+dim dimensional) state vector <code>U</code>, compute\n       * and return the derivative \\f$\\eta'\\f$ of the entropy defined above.\n       */\n      state_type mathematical_entropy_derivative(const state_type &U) const;\n\n      /**\n       * Returns whether the state @p U is admissible. If @p U is a\n       * vectorized state then @p U is admissible if all vectorized\n       * values are admissible.\n       */\n      bool is_admissible(const state_type &U) const;\n\n      //@}\n      /**\n       * @name Special functions for boundary states\n       */\n      //@{\n\n      /**\n       * Decomposes a given state @p U into Riemann invariants and then\n       * replaces the first or second Riemann characteristic from the one\n       * taken from @p U_bar state.\n       */\n      template <int component>\n      state_type prescribe_riemann_characteristic(\n          const state_type &U,\n          const state_type &U_bar,\n          const dealii::Tensor<1, dim, Number> &normal) const;\n\n      /**\n       * Apply boundary conditions.\n       */\n      template <typename Lambda>\n      state_type\n      apply_boundary_conditions(const dealii::types::boundary_id id,\n                                const state_type &U,\n                                const dealii::Tensor<1, dim, Number> &normal,\n                                const Lambda &get_dirichlet_data) const;\n\n      //@}\n      /**\n       * @name Flux computations\n       */\n      //@{\n\n      /**\n       * Given a state @p U compute the flux\n       * \\f[\n       * \\begin{pmatrix}\n       *   \\textbf m \\\\\n       *   \\textbf v\\otimes \\textbf m + p\\mathbb{I}_d \\\\\n       * \\end{pmatrix},\n       * \\f]\n       */\n      flux_type f(const state_type &U) const;\n\n      /**\n       * Given a state @p U compute the flux\n       * \\g[\n       * \\begin{pmatrix}\n       *   \\textbf m \\\\\n       *   \\textbf v\\otimes \\textbf m \\\\\n       * \\end{pmatrix},\n       * \\f]\n       */\n      flux_type g(const state_type &U) const;\n\n      /**\n       * For a given (1+dim dimensional) state vector <code>U</code> and\n       * left/right topography states <code>Z_left</code> and\n       * <code>Z_right</code>, return the star_state <code>U_star</code>\n       */\n      state_type star_state(const state_type &U,\n                            const Number &Z_left,\n                            const Number &Z_right) const;\n\n      /**\n       * Given precomputed flux contributions @p prec_i and @p prec_j\n       * compute the equilibrated states \\f$U_i^{\\ast,j}\\f$ and\n       * \\f$U_j^{\\ast,i}\\f$.\n       */\n      std::array<state_type, 2>\n      equilibrated_states(const flux_contribution_type &,\n                          const flux_contribution_type &) const;\n\n      /**\n       * Given a state @p U_i and an index @p i compute flux contributions.\n       *\n       * Intended usage:\n       * ```\n       * Indicator<dim, Number> indicator;\n       * for (unsigned int i = n_internal; i < n_owned; ++i) {\n       *   // ...\n       *   const auto flux_i = flux_contribution(precomputed..., i, U_i);\n       *   for (unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {\n       *     // ...\n       *     const auto flux_j = flux_contribution(precomputed..., js, U_j);\n       *     const auto flux_ij = flux_divergence(flux_i, flux_j, c_ij);\n       *   }\n       * }\n       * ```\n       *\n       * For the Shallow water equations we simply retrieve the\n       * bathymetry and return, both, state and bathymetry.\n       */\n      flux_contribution_type\n      flux_contribution(const PrecomputedVector &pv,\n                        const InitialPrecomputedVector &piv,\n                        const unsigned int i,\n                        const state_type &U_i) const;\n\n      flux_contribution_type\n      flux_contribution(const PrecomputedVector &pv,\n                        const InitialPrecomputedVector &piv,\n                        const unsigned int *js,\n                        const state_type &U_j) const;\n\n      /**\n       * Given precomputed flux contributions @p prec_i and @p prec_j\n       * compute the equilibrated, low-order flux \\f$(f(U_i^{\\ast,j}) +\n       * f(U_j^{\\ast,i})\\f$\n       */\n      state_type\n      flux_divergence(const flux_contribution_type &flux_i,\n                      const flux_contribution_type &flux_j,\n                      const dealii::Tensor<1, dim, Number> &c_ij) const;\n\n      /**\n       * The low-order and high-order fluxes differ:\n       */\n      static constexpr bool have_high_order_flux = true;\n\n      /**\n       * Given precomputed flux contributions @p prec_i and @p prec_j\n       * compute the high-order flux \\f$(f(U_i^{\\ast,j}) +\n       * f(U_j^{\\ast,i})\\f$\n       */\n      state_type high_order_flux_divergence(\n          const flux_contribution_type &flux_i,\n          const flux_contribution_type &flux_j,\n          const dealii::Tensor<1, dim, Number> &c_ij) const;\n\n      /**\n       * Given precomputed flux contributions @p prec_i and @p prec_j compute\n       * the equilibrated, low-order affine shift\n       * \\f$ B_{ij} = -2d_ij(U^{\\ast,j}_i)-2f((U^{\\ast,j}_i))c_ij\\f$.\n       */\n      state_type affine_shift(const flux_contribution_type &flux_i,\n                              const flux_contribution_type &flux_j,\n                              const dealii::Tensor<1, dim, Number> &c_ij,\n                              const Number &d_ij) const;\n\n      //@}\n      /**\n       * @name Computing source terms\n       */\n      //@{\n\n      /** We do have source terms */\n      static constexpr bool have_source_terms = true;\n\n      /**\n       * A regularized Gauckler-Manning friction coefficient.\n       *\n       * FIXME: details\n       */\n      state_type manning_friction(const state_type &U,\n                                  const Number &h_star,\n                                  const ScalarNumber tau) const;\n\n      state_type nodal_source(const PrecomputedVector &pv,\n                              const unsigned int i,\n                              const state_type &U_i,\n                              const ScalarNumber tau) const;\n\n      state_type nodal_source(const PrecomputedVector &pv,\n                              const unsigned int *js,\n                              const state_type &U_j,\n                              const ScalarNumber tau) const;\n\n      //@}\n      /**\n       * @name State transformations (primitive states, expanding\n       * dimensionality, Galilei transform, etc.)\n       */\n      //@{\n\n      /**\n       * Given a state vector associated with a different spatial\n       * dimensions than the current one, return an \"expanded\" version of\n       * the state vector associated with @a dim spatial dimensions where\n       * the momentum vector of the conserved state @p state is expaned\n       * with zeros to a total length of @a dim entries.\n       *\n       * @note @a dim has to be larger or equal than the dimension of the\n       * @a ST vector.\n       */\n      template <typename ST>\n      state_type expand_state(const ST &state) const;\n\n      /**\n       * Given an initial state [h, u_1, ..., u_d] return a\n       * conserved state [h, m_1, ..., m_d].\n       *\n       * This function simply calls from_primitive_state() and\n       * expand_state().\n       *\n       * @note This function is used to conveniently convert (user\n       * provided) primitive initial states to a conserved state in the\n       * ShallowWaterInitialStateLibrary.\n       */\n      template <typename ST>\n      state_type from_initial_state(const ST &initial_state) const;\n\n      /**\n       * Given a primitive state [h, u_1, ..., u_d] return a conserved\n       * state\n       */\n      state_type from_primitive_state(const state_type &primitive_state) const;\n\n      /**\n       * Given a conserved state return a primitive state [h, u_1, ..., u_d]\n       */\n      state_type to_primitive_state(const state_type &state) const;\n\n      /**\n       * Transform the current state according to a  given operator\n       * @p lambda acting on a @a dim dimensional momentum (or velocity)\n       * vector.\n       */\n      template <typename Lambda>\n      state_type apply_galilei_transform(const state_type &state,\n                                         const Lambda &lambda) const;\n\n    }; /* HyperbolicSystemView */\n\n\n    /*\n     * -------------------------------------------------------------------------\n     * Inline definitions\n     * -------------------------------------------------------------------------\n     */\n\n\n    inline HyperbolicSystem::HyperbolicSystem(const std::string &subsection)\n        : ParameterAcceptor(subsection)\n    {\n      gravity_ = 9.81;\n      add_parameter(\"gravity\", gravity_, \"Gravitational constant [m/s^2]\");\n\n      manning_friction_coefficient_ = 0.;\n      add_parameter(\"manning friction coefficient\",\n                    manning_friction_coefficient_,\n                    \"Roughness coefficient for friction source\");\n\n      reference_water_depth_ = 1.;\n      add_parameter(\"reference water depth\",\n                    reference_water_depth_,\n                    \"Problem specific water depth reference\");\n\n      dry_state_relaxation_small_ = 1.e2;\n      add_parameter(\"dry state relaxation small\",\n                    dry_state_relaxation_small_,\n                    \"Problem specific dry-state relaxation parameter\");\n\n      dry_state_relaxation_large_ = 1.e4;\n      add_parameter(\"dry state relaxation large\",\n                    dry_state_relaxation_large_,\n                    \"Problem specific dry-state relaxation parameter\");\n    }\n\n\n    template <int dim, typename ScalarNumber>\n    inline void HyperbolicSystem::fill_precomputed_values(\n        const OfflineData<dim, ScalarNumber> &offline_data,\n        typename HyperbolicSystemView<dim, ScalarNumber>::StateVector\n            &state_vector,\n        const bool skip_constrained_dofs) const\n    {\n      const unsigned int n_internal = offline_data.n_locally_internal();\n      const unsigned int n_owned = offline_data.n_locally_owned();\n      const auto &sparsity_simd = offline_data.sparsity_pattern_simd();\n      using VA = dealii::VectorizedArray<ScalarNumber>;\n\n      const auto &U = std::get<0>(state_vector);\n      auto &precomputed = std::get<1>(state_vector);\n\n      const auto body = [&](auto sentinel, unsigned int i) {\n        using T = decltype(sentinel);\n        using View = HyperbolicSystemView<dim, T>;\n        using precomputed_type = typename View::precomputed_type;\n\n        const unsigned int row_length = sparsity_simd.row_length(i);\n        if (skip_constrained_dofs && row_length == 1)\n          return;\n\n        const auto U_i = U.template read_tensor<T>(i);\n        const auto view = this->view<dim, T>();\n        const auto eta_m = view.mathematical_entropy(U_i);\n        const auto h_sharp = view.water_depth_sharp(U_i);\n        const auto h_star = ryujin::pow(h_sharp, ScalarNumber(4. / 3.));\n\n        const precomputed_type prec_i{eta_m, h_star};\n\n        precomputed.template write_tensor<T>(prec_i, i);\n      };\n\n      cpu_simd_loop<ScalarNumber>(\"time_step_1\", body, 0, n_internal, n_owned);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::water_depth(const state_type &U)\n    {\n      return U[0];\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::inverse_water_depth_mollified(\n        const state_type &U) const\n    {\n      constexpr ScalarNumber eps = std::numeric_limits<ScalarNumber>::epsilon();\n\n      const Number h_cutoff_mollified =\n          reference_water_depth() * dry_state_relaxation_large() * Number(eps);\n\n      const Number h = water_depth(U);\n      const Number h_pos = positive_part(water_depth(U));\n      const Number h_max = std::max(h, h_cutoff_mollified);\n      const Number denom = h * h + h_max * h_max;\n      return ScalarNumber(2.) * h_pos / denom;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::water_depth_sharp(\n        const state_type &U) const\n    {\n      constexpr ScalarNumber eps = std::numeric_limits<ScalarNumber>::epsilon();\n\n      const Number h_cutoff_small =\n          reference_water_depth() * dry_state_relaxation_small() * Number(eps);\n\n      const Number h = water_depth(U);\n      const Number h_max = std::max(h, h_cutoff_small);\n      return h_max;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::inverse_water_depth_sharp(\n        const state_type &U) const\n    {\n      return ScalarNumber(1.) / water_depth_sharp(U);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::filter_dry_water_depth(\n        const Number &h) const\n    {\n      using ScalarNumber = typename get_value_type<Number>::type;\n      constexpr ScalarNumber eps = std::numeric_limits<ScalarNumber>::epsilon();\n\n      const Number h_cutoff_large =\n          reference_water_depth() * dry_state_relaxation_large() * Number(eps);\n\n      return dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(\n          std::abs(h), h_cutoff_large, Number(0.), h);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline dealii::Tensor<1, dim, Number>\n    HyperbolicSystemView<dim, Number>::momentum(const state_type &U)\n    {\n      dealii::Tensor<1, dim, Number> result;\n\n      for (unsigned int i = 0; i < dim; ++i)\n        result[i] = U[1 + i];\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::kinetic_energy(const state_type &U) const\n    {\n      const auto h = water_depth(U);\n      const auto vel = momentum(U) * inverse_water_depth_sharp(U);\n\n      /* KE = 1/2 h |v|^2 */\n      return ScalarNumber(0.5) * h * vel.norm_square();\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::pressure(const state_type &U) const\n    {\n      const Number h_sqd = U[0] * U[0];\n\n      /* p = 1/2 g h^2 */\n      return ScalarNumber(0.5) * gravity() * h_sqd;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::speed_of_sound(const state_type &U) const\n    {\n      /* c^2 = g * h */\n      return std::sqrt(gravity() * U[0]);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    HyperbolicSystemView<dim, Number>::mathematical_entropy(\n        const state_type &U) const\n    {\n      const auto p = pressure(U);\n      const auto k_e = kinetic_energy(U);\n      return p + k_e;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::mathematical_entropy_derivative(\n        const state_type &U) const -> state_type\n    {\n      /*\n       * With\n       *   eta = 1/2 g h^2 + 1/2 |m|^2 / h\n       *\n       * we get\n       *\n       *   eta' = (g h - 1/2 |vel|^2, vel)\n       *\n       * where vel = m / h\n       */\n\n      state_type result;\n\n      const Number &h = U[0];\n      const auto vel = momentum(U) * inverse_water_depth_sharp(U);\n\n      // water depth component\n      result[0] = gravity() * h - ScalarNumber(0.5) * vel.norm_square();\n\n      // momentum components\n      for (unsigned int i = 0; i < dim; ++i) {\n        result[1 + i] = vel[i];\n      }\n\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline bool\n    HyperbolicSystemView<dim, Number>::is_admissible(const state_type &U) const\n    {\n      const auto h = filter_dry_water_depth(water_depth(U));\n\n      constexpr auto gte = dealii::SIMDComparison::greater_than_or_equal;\n      const auto test = dealii::compare_and_apply_mask<gte>(\n          h, Number(0.), Number(0.), Number(-1.));\n\n#ifdef DEBUG_OUTPUT\n      if (!(test == Number(0.))) {\n        std::cout << std::fixed << std::setprecision(16);\n        std::cout << \"Bounds violation: Negative state [h] detected!\\n\";\n        std::cout << \"\\t\\th: \" << h << \"\\n\" << std::endl;\n        __builtin_trap();\n      }\n#endif\n\n      return (test == Number(0.));\n    }\n\n\n    template <int dim, typename Number>\n    template <int component>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::prescribe_riemann_characteristic(\n        const state_type &U,\n        const state_type &U_bar,\n        const dealii::Tensor<1, dim, Number> &normal) const -> state_type\n    {\n      /* Note that U_bar are the dirichlet values that are prescribed */\n      static_assert(component == 1 || component == 2,\n                    \"component has to be 1 or 2\");\n\n      using ScalarNumber = typename get_value_type<Number>::type;\n\n      const auto m = momentum(U);\n      const auto a = speed_of_sound(U);\n      const auto vn = m * normal * inverse_water_depth_sharp(U);\n\n      const auto m_bar = momentum(U_bar);\n      const auto a_bar = speed_of_sound(U_bar);\n      const auto vn_bar = m_bar * normal * inverse_water_depth_sharp(U_bar);\n\n      /* First Riemann characteristic: v * n - 2 * a */\n\n      const auto R_1 = component == 1 ? vn_bar - ScalarNumber(2.) * a_bar\n                                      : vn - ScalarNumber(2.) * a;\n\n      /* Second Riemann characteristic: v * n + 2 * a */\n\n      const auto R_2 = component == 2 ? vn_bar + ScalarNumber(2.) * a_bar\n                                      : vn + ScalarNumber(2.) * a;\n\n      const auto vperp = m * inverse_water_depth_sharp(U) - vn * normal;\n\n      const auto vn_new = ScalarNumber(0.5) * (R_1 + R_2);\n\n      const auto h_new =\n          ryujin::fixed_power<2>((R_2 - R_1) / ScalarNumber(4.)) / gravity();\n\n      state_type U_new;\n      U_new[0] = h_new;\n      for (unsigned int d = 0; d < dim; ++d) {\n        U_new[1 + d] = h_new * (vn_new * normal + vperp)[d];\n      }\n\n      return U_new;\n    }\n\n\n    template <int dim, typename Number>\n    template <typename Lambda>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::apply_boundary_conditions(\n        const dealii::types::boundary_id id,\n        const state_type &U,\n        const dealii::Tensor<1, dim, Number> &normal,\n        const Lambda &get_dirichlet_data) const -> state_type\n    {\n      state_type result = U;\n\n      if (id == Boundary::dirichlet) {\n        result = get_dirichlet_data();\n\n      } else if (id == Boundary::dirichlet_momentum) {\n        /* Only enforce Dirichlet conditions on the momentum: */\n        auto m_dirichlet = momentum(get_dirichlet_data());\n        for (unsigned int k = 0; k < dim; ++k)\n          result[k + 1] = m_dirichlet[k];\n\n      } else if (id == Boundary::dirichlet_velocity) {\n        /* Only enforce Dirichlet conditions on the velocity: */\n        const auto U_dirichlet = get_dirichlet_data();\n        const auto h_inv_sharp_dirichlet =\n            inverse_water_depth_sharp(U_dirichlet);\n        const auto v_dirichlet = momentum(U_dirichlet) * h_inv_sharp_dirichlet;\n        const auto h = water_depth_sharp(result);\n        for (unsigned int k = 0; k < dim; ++k)\n          result[k + 1] = h * v_dirichlet[k];\n\n      } else if (id == Boundary::slip) {\n        auto m = momentum(U);\n        m -= 1. * (m * normal) * normal;\n        for (unsigned int k = 0; k < dim; ++k)\n          result[k + 1] = m[k];\n\n      } else if (id == Boundary::no_slip) {\n        for (unsigned int k = 0; k < dim; ++k)\n          result[k + 1] = Number(0.);\n\n      } else if (id == Boundary::dynamic) {\n        /*\n         * On dynamic boundary conditions, we distinguish four cases:\n         *\n         *  - supersonic inflow: prescribe full state\n         *  - subsonic inflow:\n         *      decompose into Riemann invariants and leave R_2\n         *      characteristic untouched.\n         *  - supersonic outflow: do nothing\n         *  - subsonic outflow:\n         *      decompose into Riemann invariants and prescribe incoming\n         *      R_1 characteristic.\n         */\n        const auto m = momentum(U);\n        const auto h_inverse = inverse_water_depth_sharp(U);\n        const auto a = speed_of_sound(U);\n        const auto vn = m * normal * h_inverse;\n\n        /* Supersonic inflow: */\n        if (vn < -a) {\n          result = get_dirichlet_data();\n        }\n\n        /* Subsonic inflow: */\n        if (vn >= -a && vn <= 0.) {\n          const auto U_dirichlet = get_dirichlet_data();\n          result = prescribe_riemann_characteristic<2>(U_dirichlet, U, normal);\n        }\n\n        /* Subsonic outflow: */\n        if (vn > 0. && vn <= a) {\n          const auto U_dirichlet = get_dirichlet_data();\n          result = prescribe_riemann_characteristic<1>(U, U_dirichlet, normal);\n        }\n\n        /* Supersonic outflow: do nothing, i.e., keep U as is */\n\n      } else {\n        AssertThrow(false, dealii::ExcNotImplemented());\n      }\n\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::f(const state_type &U) const -> flux_type\n    {\n      const auto h_inverse = inverse_water_depth_sharp(U);\n      const auto m = momentum(U);\n      const auto p = pressure(U);\n\n      flux_type result;\n\n      result[0] = (m * h_inverse) * U[0];\n      for (unsigned int i = 0; i < dim; ++i) {\n        result[1 + i] = (m * h_inverse) * m[i];\n        result[1 + i][i] += p;\n      }\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::g(const state_type &U) const -> flux_type\n    {\n      const auto h_inverse = inverse_water_depth_sharp(U);\n      const auto m = momentum(U);\n\n      flux_type result;\n\n      result[0] = (m * h_inverse) * U[0];\n      for (unsigned int i = 0; i < dim; ++i) {\n        result[1 + i] = (m * h_inverse) * m[i];\n      }\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::star_state(const state_type &U,\n                                                  const Number &Z_left,\n                                                  const Number &Z_right) const\n        -> state_type\n    {\n      const Number Z_max = std::max(Z_left, Z_right);\n      const Number h = water_depth(U);\n      const Number H_star = std::max(Number(0.), h + Z_left - Z_max);\n\n      return U * H_star * inverse_water_depth_mollified(U);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::equilibrated_states(\n        const flux_contribution_type &flux_i,\n        const flux_contribution_type &flux_j) const -> std::array<state_type, 2>\n    {\n      const auto &[U_i, Z_i] = flux_i;\n      const auto &[U_j, Z_j] = flux_j;\n\n      const auto U_star_ij = star_state(U_i, Z_i, Z_j);\n      const auto U_star_ji = star_state(U_j, Z_j, Z_i);\n\n      return {U_star_ij, U_star_ji};\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::flux_contribution(\n        const PrecomputedVector & /*pv*/,\n        const InitialPrecomputedVector &piv,\n        const unsigned int i,\n        const state_type &U_i) const -> flux_contribution_type\n    {\n      const auto Z_i = piv.template read_tensor<Number>(i)[0];\n      return {U_i, Z_i};\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::flux_contribution(\n        const PrecomputedVector & /*pv*/,\n        const InitialPrecomputedVector &piv,\n        const unsigned int *js,\n        const state_type &U_j) const -> flux_contribution_type\n    {\n      const auto Z_j = piv.template read_tensor<Number>(js)[0];\n      return {U_j, Z_j};\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::flux_divergence(\n        const flux_contribution_type &flux_i,\n        const flux_contribution_type &flux_j,\n        const dealii::Tensor<1, dim, Number> &c_ij) const -> state_type\n    {\n      const auto &[U_i, Z_i] = flux_i;\n      const auto &[U_star_ij, U_star_ji] = equilibrated_states(flux_i, flux_j);\n\n      const auto H_i = water_depth(U_i);\n      const auto H_star_ij = water_depth(U_star_ij);\n      const auto H_star_ji = water_depth(U_star_ji);\n\n      const auto g_i = g(U_star_ij);\n      const auto g_j = g(U_star_ji);\n\n      auto result = -add(g_i, g_j);\n\n      const auto factor =\n          (ScalarNumber(0.5) * (H_star_ji * H_star_ji - H_star_ij * H_star_ij) +\n           H_i * H_i) *\n          gravity();\n\n      for (unsigned int i = 0; i < dim; ++i) {\n        result[1 + i][i] -= factor;\n      }\n\n      return contract(result, c_ij);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::high_order_flux_divergence(\n        const flux_contribution_type &flux_i,\n        const flux_contribution_type &flux_j,\n        const dealii::Tensor<1, dim, Number> &c_ij) const -> state_type\n    {\n      const auto &[U_i, Z_i] = flux_i;\n      const auto &[U_j, Z_j] = flux_j;\n\n      const auto H_i = water_depth(U_i);\n      const auto H_j = water_depth(U_j);\n\n      const auto g_i = g(U_i);\n      const auto g_j = g(U_j);\n\n      auto result = -add(g_i, g_j);\n\n      const auto factor = gravity() * H_i * (H_j + Z_j - Z_i);\n      for (unsigned int i = 0; i < dim; ++i) {\n        result[1 + i][i] -= factor;\n      }\n\n      return contract(result, c_ij);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::affine_shift(\n        const flux_contribution_type &flux_i,\n        const flux_contribution_type &flux_j,\n        const dealii::Tensor<1, dim, Number> &c_ij,\n        const Number &d_ij) const -> state_type\n    {\n      const auto &[U_i, Z_i] = flux_i;\n      const auto &[U_j, Z_j] = flux_j;\n      const auto U_star_ij = star_state(U_i, Z_i, Z_j);\n\n      const auto h_inverse = inverse_water_depth_sharp(U_i);\n      const auto m = momentum(U_i);\n      const auto factor = ScalarNumber(2.) * (d_ij + h_inverse * (m * c_ij));\n\n      return -factor * (U_star_ij - U_i);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::manning_friction(\n        const state_type &U, const Number &h_star, const ScalarNumber tau) const\n        -> state_type\n    {\n      state_type result;\n\n      const auto g = gravity();\n      const auto n = manning_friction_coefficient();\n\n      const auto h_inverse = inverse_water_depth_mollified(U);\n\n      const auto m = momentum(U);\n      const auto v_norm = (m * h_inverse).norm();\n      const auto factor = ScalarNumber(2.) * g * n * n * v_norm;\n\n      const auto denominator = h_star + std::max(h_star, tau * factor);\n      const auto denominator_inverse = ScalarNumber(1.) / denominator;\n\n      for (unsigned int d = 0; d < dim; ++d)\n        result[d + 1] = -factor * denominator_inverse * m[d];\n\n      return result;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::nodal_source(\n        const PrecomputedVector &pv,\n        const unsigned int i,\n        const state_type &U_i,\n        const ScalarNumber tau) const -> state_type\n    {\n      const auto &[eta_m, h_star] =\n          pv.template read_tensor<Number, precomputed_type>(i);\n\n      return manning_friction(U_i, h_star, tau);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::nodal_source(\n        const PrecomputedVector &pv,\n        const unsigned int *js,\n        const state_type &U_j,\n        const ScalarNumber tau) const -> state_type\n    {\n      const auto &[eta_m, h_star] =\n          pv.template read_tensor<Number, precomputed_type>(js);\n\n      return manning_friction(U_j, h_star, tau);\n    }\n\n\n    template <int dim, typename Number>\n    template <typename ST>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::expand_state(const ST &state) const\n        -> state_type\n    {\n      using T = typename ST::value_type;\n      static_assert(std::is_same_v<Number, T>, \"template mismatch\");\n\n      constexpr auto dim2 = ST::dimension - 1;\n      static_assert(dim >= dim2,\n                    \"the space dimension of the argument state must not be \"\n                    \"larger than the one of the target state\");\n\n      state_type result;\n      result[0] = state[0];\n      for (unsigned int i = 1; i < dim2 + 1; ++i)\n        result[i] = state[i];\n\n      return result;\n    }\n\n    template <int dim, typename Number>\n    template <typename ST>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::from_initial_state(\n        const ST &initial_state) const -> state_type\n    {\n      const auto primitive_state = expand_state(initial_state);\n      return from_primitive_state(primitive_state);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::from_primitive_state(\n        const state_type &primitive_state) const -> state_type\n    {\n      const auto &h = primitive_state[0];\n\n      auto state = primitive_state;\n      /* Fix up momentum: */\n      for (unsigned int i = 1; i < dim + 1; ++i)\n        state[i] *= h;\n\n      return state;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::to_primitive_state(\n        const state_type &state) const -> state_type\n    {\n      const auto h_inverse = inverse_water_depth_sharp(state);\n\n      auto primitive_state = state;\n      /* Fix up velocity: */\n      for (unsigned int i = 1; i < dim + 1; ++i)\n        primitive_state[i] *= h_inverse;\n\n      return primitive_state;\n    }\n\n\n    template <int dim, typename Number>\n    template <typename Lambda>\n    DEAL_II_ALWAYS_INLINE inline auto\n    HyperbolicSystemView<dim, Number>::apply_galilei_transform(\n        const state_type &state, const Lambda &lambda) const -> state_type\n    {\n      auto result = state;\n      auto M = lambda(momentum(state));\n      for (unsigned int d = 0; d < dim; ++d)\n        result[1 + d] = M[d];\n      return result;\n    }\n\n  } // namespace ShallowWater\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/indicator.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2023 - 2025 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"hyperbolic_system.h\"\n\n#include <multicomponent_vector.h>\n\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/vectorization.h>\n\n\nnamespace ryujin\n{\n  namespace ShallowWater\n  {\n    template <typename ScalarNumber = double>\n    class IndicatorParameters : public dealii::ParameterAcceptor\n    {\n    public:\n      IndicatorParameters(const std::string &subsection = \"/Indicator\")\n          : ParameterAcceptor(subsection)\n      {\n        evc_factor_ = ScalarNumber(1.);\n        add_parameter(\"evc factor\",\n                      evc_factor_,\n                      \"Factor for scaling the entropy viscocity commuator\");\n      }\n\n      ACCESSOR_READ_ONLY(evc_factor);\n\n    private:\n      ScalarNumber evc_factor_;\n    };\n\n\n    /**\n     * An suitable indicator strategy that is used to form the preliminary\n     * high-order update.\n     *\n     * @ingroup ShallowWaterEquations\n     */\n    template <int dim, typename Number = double>\n    class Indicator\n    {\n    public:\n      /**\n       * @name Typedefs and constexpr constants\n       */\n      //@{\n\n      using View = HyperbolicSystemView<dim, Number>;\n\n      using ScalarNumber = typename View::ScalarNumber;\n\n      static constexpr auto problem_dimension = View::problem_dimension;\n\n      using state_type = typename View::state_type;\n\n      using flux_type = typename View::flux_type;\n\n      using precomputed_type = typename View::precomputed_type;\n\n      using PrecomputedVector = typename View::PrecomputedVector;\n\n      using Parameters = IndicatorParameters<ScalarNumber>;\n\n      //@}\n      /**\n       * @name Stencil-based computation of indicators\n       *\n       * Intended usage:\n       * ```\n       * Indicator<dim, Number> indicator;\n       * for (unsigned int i = n_internal; i < n_owned; ++i) {\n       *   // ...\n       *   indicator.reset(i, U_i);\n       *   for (unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {\n       *     // ...\n       *     indicator.accumulate(js, U_j, c_ij);\n       *   }\n       *   indicator.alpha(hd_i);\n       * }\n       * ```\n       */\n      //@{\n\n      /**\n       * Constructor taking a HyperbolicSystem instance as argument\n       */\n      Indicator(const HyperbolicSystem &hyperbolic_system,\n                const Parameters &parameters,\n                const PrecomputedVector &precomputed_values)\n          : hyperbolic_system(hyperbolic_system)\n          , parameters(parameters)\n          , precomputed_values(precomputed_values)\n      {\n      }\n\n      /**\n       * Reset temporary storage and initialize for a new row corresponding\n       * to state vector U_i.\n       */\n      void reset(const unsigned int /*i*/, const state_type &U_i);\n\n      /**\n       * When looping over the sparsity row, add the contribution associated\n       * with the neighboring state U_j.\n       */\n      void accumulate(const unsigned int *js,\n                      const state_type &U_j,\n                      const dealii::Tensor<1, dim, Number> &c_ij);\n\n      /**\n       * Return the computed alpha_i value.\n       */\n      Number alpha(const Number h_i);\n\n      //@}\n\n    private:\n      /**\n       * @name\n       */\n      //@{\n\n      const HyperbolicSystem &hyperbolic_system;\n      const Parameters &parameters;\n      const PrecomputedVector &precomputed_values;\n\n      Number h_i = 0.;\n      Number eta_i = 0.;\n      flux_type f_i;\n      state_type d_eta_i;\n      Number pressure_i = 0.;\n\n      Number left = 0.;\n      state_type right;\n      //@}\n    };\n\n\n    /*\n     * -------------------------------------------------------------------------\n     * Inline definitions\n     * -------------------------------------------------------------------------\n     */\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline void\n    Indicator<dim, Number>::reset(const unsigned int i, const state_type &U_i)\n    {\n      /* entropy viscosity commutator: */\n\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      const auto &[eta_m, h_star] =\n          precomputed_values.template read_tensor<Number, precomputed_type>(i);\n\n      h_i = view.water_depth(U_i);\n      eta_i = eta_m;\n      d_eta_i = view.mathematical_entropy_derivative(U_i);\n      f_i = view.f(U_i);\n      pressure_i = view.pressure(U_i);\n\n      left = 0.;\n      right = 0.;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline void Indicator<dim, Number>::accumulate(\n        const unsigned int *js,\n        const state_type &U_j,\n        const dealii::Tensor<1, dim, Number> &c_ij)\n    {\n      /* entropy viscosity commutator: */\n\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      const auto &[eta_j, h_star_j] =\n          precomputed_values.template read_tensor<Number, precomputed_type>(js);\n\n      const auto velocity_j =\n          view.momentum(U_j) * view.inverse_water_depth_sharp(U_j);\n      const auto f_j = view.f(U_j);\n      const auto pressure_j = view.pressure(U_j);\n\n      left += (eta_j + pressure_j) * (velocity_j * c_ij);\n\n      for (unsigned int k = 0; k < problem_dimension; ++k)\n        right[k] += (f_j[k] - f_i[k]) * c_ij;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    Indicator<dim, Number>::alpha(const Number hd_i)\n    {\n      Number my_sum = 0.;\n      for (unsigned int k = 0; k < problem_dimension; ++k) {\n        my_sum += d_eta_i[k] * right[k];\n      }\n\n      Number numerator = std::abs(left - my_sum);\n      Number denominator = std::abs(left) + std::abs(my_sum);\n\n      const auto regularization =\n          Number(100. * std::numeric_limits<ScalarNumber>::min());\n\n      const auto quotient =\n          std::abs(numerator) /\n          (denominator + std::max(hd_i * std::abs(eta_i), regularization));\n\n      return std::min(Number(1.), parameters.evc_factor() * quotient);\n    }\n\n\n  } // namespace ShallowWater\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/initial_state_circular_dam_break.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2022 - 2025 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"hyperbolic_system.h\"\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  namespace ShallowWater\n  {\n    struct Description;\n\n    /**\n     * Circular dam break problem.\n     *\n     * @ingroup ShallowWaterEquations\n     */\n    template <int dim, typename Number>\n    class CircularDamBreak : public InitialState<Description, dim, Number>\n    {\n    public:\n      using View = HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n\n      CircularDamBreak(const HyperbolicSystem &hyperbolic_system,\n                       const std::string sub)\n          : InitialState<Description, dim, Number>(\"circular dam break\", sub)\n          , hyperbolic_system(hyperbolic_system)\n      {\n        still_water_depth_ = 0.5;\n        this->add_parameter(\"still water depth\",\n                            still_water_depth_,\n                            \"Depth of still water outside circular dam\");\n        radius_ = 2.5;\n        this->add_parameter(\"radius\", radius_, \"Radius of circular dam \");\n\n        dam_amplitude_ = 2.5;\n        this->add_parameter(\n            \"dam amplitude\", dam_amplitude_, \"Amplitude of circular dam\");\n      }\n\n      state_type compute(const dealii::Point<dim> &point, Number /*t*/) final\n      {\n        const Number r = point.norm_square();\n        const Number h = (r <= radius_ ? dam_amplitude_ : still_water_depth_);\n\n        return state_type{{h, 0.}};\n      }\n\n      /* Default bathymetry of 0 */\n\n    private:\n      const HyperbolicSystem &hyperbolic_system;\n\n      Number still_water_depth_;\n      Number radius_;\n      Number dam_amplitude_;\n    };\n\n  } // namespace ShallowWater\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/initial_state_contrast.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2022 - 2025 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  namespace ShallowWaterInitialStates\n  {\n    /**\n     * An initial state formed by a contrast of a given \"left\" and \"right\"\n     * primitive state.\n     *\n     * @ingroup ShallowWaterEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class Contrast : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n\n      Contrast(const HyperbolicSystem &hyperbolic_system,\n               const std::string subsection)\n          : InitialState<Description, dim, Number>(\"contrast\", subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n        primitive_left_[0] = 1.;\n        primitive_left_[1] = 0.0;\n        this->add_parameter(\"primitive state left\",\n                            primitive_left_,\n                            \"Initial 1d primitive state (h, u) on the left\");\n\n        primitive_right_[0] = 1.;\n        primitive_right_[1] = 0.0;\n        this->add_parameter(\"primitive state right\",\n                            primitive_right_,\n                            \"Initial 1d primitive state (h, u) on the right\");\n      }\n\n      state_type compute(const dealii::Point<dim> &point, Number /*t*/) final\n      {\n        const auto view = hyperbolic_system_.template view<dim, Number>();\n        const auto temp = point[0] > 0. ? primitive_right_ : primitive_left_;\n        return view.from_initial_state(temp);\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n      dealii::Tensor<1, 2, Number> primitive_left_;\n      dealii::Tensor<1, 2, Number> primitive_right_;\n    };\n  } // namespace ShallowWaterInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/initial_state_flow_over_bump.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2022 - 2025 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\n#include <simd.h>\n\nnamespace ryujin\n{\n  namespace ShallowWaterInitialStates\n  {\n    /**\n     * Flow over a bump with a hydraulic jump.\n     * See Section 7.2 in @cite GuermondEtAl2018SW for details.\n     *\n     * @ingroup ShallowWaterEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class FlowOverBump : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n\n      FlowOverBump(const HyperbolicSystem &hyperbolic_system,\n                   const std::string subsection)\n          : InitialState<Description, dim, Number>(\"flow over bump\", subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n        dealii::ParameterAcceptor::parse_parameters_call_back.connect(\n            std::bind(&FlowOverBump::parse_parameters_callback, this));\n\n        which_case_ = \"transcritical\";\n        this->add_parameter(\"flow type\",\n                            which_case_,\n                            \"Either 'transcritical' flow with shock \"\n                            \"or 'subsonic' flow.\");\n      }\n\n      void parse_parameters_callback()\n      {\n        AssertThrow(which_case_ == \"subsonic\" || which_case_ == \"transcritical\",\n                    dealii::ExcMessage(\"Flow type must be 'transcritical' \"\n                                       \"or 'subsonic'. \"));\n      }\n\n      state_type compute(const dealii::Point<dim> &point, Number t) final\n      {\n        const auto view = hyperbolic_system_.template view<dim, Number>();\n        const auto g = view.gravity();\n\n        const auto x = point[0];\n\n        /* Define constants for transcritical flow */\n        const Number xM = 10.;\n        const Number xS = 11.7;\n        const Number zM = 0.2;\n        Number h_inflow = 0.28205279813802181;\n        Number q_inflow = 0.18;\n        Number cBer =\n            zM + 1.5 * ryujin::pow(q_inflow * q_inflow / g, Number(1. / 3.));\n\n\n        /* Subsonic flow constants */\n        if (which_case_ == \"subsonic\") {\n          q_inflow = 4.42;\n          h_inflow = 2.;\n          cBer = std::pow(q_inflow / h_inflow, 2) / (2. * g) + h_inflow;\n        }\n\n        /* General values for Cardano's formula */\n        const Number d = q_inflow * q_inflow / (2. * g);\n        const Number b = compute_bathymetry(point) - cBer;\n        const Number Q = -std::pow(b, 2) / 9.;\n        const Number R = -(27. * d + 2. * std::pow(b, 3)) / 54.;\n        const Number theta = acos(ryujin::pow(-Q, Number(-1.5)) * R);\n\n        /* Define initial and exact solution */\n        const Number h_initial = h_inflow - compute_bathymetry(point);\n\n        /* Explicitly return initial state */\n        if (t < 1e-12) {\n          return state_type{{h_initial, q_inflow}};\n        }\n\n        Number h_exact = 2. * std::sqrt(-Q) * cos(theta / 3.) - b / 3.;\n        if (which_case_ == \"transcritical\") {\n          if (xM <= x && x < xS) {\n            h_exact = 2. * std::sqrt(-Q) *\n                          cos((4. * dealii::numbers::PI + theta) / 3.) -\n                      b / 3.;\n          } else if (xS < x) {\n            h_exact = 0.28205279813802181;\n          }\n        }\n\n        return state_type{{h_exact, q_inflow}};\n      }\n\n      auto initial_precomputations(const dealii::Point<dim> &point) ->\n          typename InitialState<Description, dim, Number>::\n              initial_precomputed_type final\n      {\n        /* Compute bathymetry: */\n        return {compute_bathymetry(point)};\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n      DEAL_II_ALWAYS_INLINE\n      inline Number compute_bathymetry(const dealii::Point<dim> &point) const\n      {\n        const auto x = point[0];\n\n        const Number bump = Number(0.2 / 64.) * std::pow(x - Number(8.), 3) *\n                            std::pow(Number(12.) - x, 3);\n\n        Number bath = 0.;\n        if (8. <= x && x <= 12.)\n          bath = bump;\n        return bath;\n      }\n\n      std::string which_case_;\n    };\n\n  } // namespace ShallowWaterInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/initial_state_function.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2023 - 2025 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\n#include <deal.II/base/function_parser.h>\n\nnamespace ryujin\n{\n  namespace ShallowWaterInitialStates\n  {\n    /**\n     * Returns an initial state defined by a set of user specified functions\n     * based on the primitive variables and the bathymetry.\n     *\n     * @ingroup ShallowWaterEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class Function : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n\n      Function(const HyperbolicSystem &hyperbolic_system,\n               const std::string subsection)\n          : InitialState<Description, dim, Number>(\"function\", subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n\n        depth_expression_ = \"1.4\";\n        this->add_parameter(\n            \"water elevation expression\",\n            depth_expression_,\n            \"A function expression describing the water elevation. When \"\n            \"bathymetry is 0, this reduces to the water depth.\");\n\n        bathymetry_expression_ = \"0.\";\n        this->add_parameter(\"bathymetry expression\",\n                            bathymetry_expression_,\n                            \"A function expression describing the bathymetry\");\n\n\n        velocity_x_expression_ = \"3.0\";\n        this->add_parameter(\n            \"velocity x expression\",\n            velocity_x_expression_,\n            \"A function expression describing the x-component of the velocity\");\n\n        if constexpr (dim > 1) {\n          velocity_y_expression_ = \"0.0\";\n          this->add_parameter(\"velocity y expression\",\n                              velocity_y_expression_,\n                              \"A function expression describing the \"\n                              \"y-component of the velocity\");\n        }\n\n        /*\n         * Set up the muparser object with the final flux description from\n         * the parameter file:\n         */\n        const auto set_up_muparser = [this] {\n          using FP = dealii::FunctionParser<dim>;\n          /*\n           * This variant of the constructor initializes the function\n           * parser with support for a time-dependent description involving\n           * a variable »t«:\n           */\n          depth_function_ = std::make_unique<FP>(depth_expression_);\n          velocity_x_function_ = std::make_unique<FP>(velocity_x_expression_);\n          if constexpr (dim > 1)\n            velocity_y_function_ = std::make_unique<FP>(velocity_y_expression_);\n          bathymetry_function_ = std::make_unique<FP>(bathymetry_expression_);\n        };\n\n        set_up_muparser();\n        this->parse_parameters_call_back.connect(set_up_muparser);\n      }\n\n      state_type compute(const dealii::Point<dim> &point, Number t) final\n      {\n        const auto view = hyperbolic_system_.template view<dim, Number>();\n        state_type full_primitive;\n\n        /* Compute bathymetry */\n        const Number z = compute_bathymetry(point);\n\n        depth_function_->set_time(t);\n        full_primitive[0] =\n            std::max(Number(depth_function_->value(point)) - z, Number(0.));\n\n        velocity_x_function_->set_time(t);\n        full_primitive[1] = velocity_x_function_->value(point);\n\n        if constexpr (dim > 1) {\n          velocity_y_function_->set_time(t);\n          full_primitive[2] = velocity_y_function_->value(point);\n        }\n\n        return view.from_primitive_state(full_primitive);\n      }\n\n      auto initial_precomputations(const dealii::Point<dim> &point) ->\n          typename InitialState<Description, dim, Number>::\n              initial_precomputed_type final\n      {\n        /* Compute bathymetry: */\n        return {compute_bathymetry(point)};\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n      std::string depth_expression_;\n      std::string velocity_x_expression_;\n      std::string velocity_y_expression_;\n      std::string bathymetry_expression_;\n\n      std::unique_ptr<dealii::FunctionParser<dim>> depth_function_;\n      std::unique_ptr<dealii::FunctionParser<dim>> velocity_x_function_;\n      std::unique_ptr<dealii::FunctionParser<dim>> velocity_y_function_;\n      std::unique_ptr<dealii::FunctionParser<dim>> bathymetry_function_;\n\n      DEAL_II_ALWAYS_INLINE inline Number\n      compute_bathymetry(const dealii::Point<dim> &point) const\n      {\n        bathymetry_function_->set_time(0.);\n        return bathymetry_function_->value(point);\n      }\n    };\n  } // namespace ShallowWaterInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/initial_state_geotiff.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2024 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <geotiff_reader.h>\n#include <initial_state_library.h>\n#include <lazy.h>\n\n#include <deal.II/base/function_parser.h>\n\nnamespace ryujin\n{\n  namespace ShallowWaterInitialStates\n  {\n    /**\n     * Returns an initial state by reading a bathymetry from a geotiff\n     * file. For this we link against GDAL, see https://gdal.org/index.html\n     * for details on GDAL and what image formats it supports.\n     *\n     * @ingroup ShallowWaterEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class GeoTIFF : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n\n\n      GeoTIFF(const HyperbolicSystem &hyperbolic_system,\n              const std::string subsection)\n          : InitialState<Description, dim, Number>(\"geotiff\", subsection)\n          , hyperbolic_system_(hyperbolic_system)\n          , geotiff_reader_(subsection + \"/geotiff\")\n      {\n        height_expression_ = \"1.4\";\n        this->add_parameter(\n            \"water height expression\",\n            height_expression_,\n            \"A function expression describing the initial total water height\");\n\n        velocity_expression_ = \"0.0\";\n        this->add_parameter(\n            \"velocity expression\",\n            velocity_expression_,\n            \"A function expression describing the initial velocity\");\n\n        const auto set_up = [this] {\n          using FP = dealii::FunctionParser<dim>;\n          /*\n           * This variant of the constructor initializes the function\n           * parser with support for a time-dependent description involving\n           * a variable »t«:\n           */\n          height_function_ = std::make_unique<FP>(height_expression_);\n          velocity_function_ = std::make_unique<FP>(velocity_expression_);\n        };\n\n        set_up();\n        this->parse_parameters_call_back.connect(set_up);\n      }\n\n      state_type compute(const dealii::Point<dim> &point, Number t) final\n      {\n        const auto z = geotiff_reader_.compute_height(point);\n\n        dealii::Tensor<1, 2, Number> primitive;\n\n        height_function_->set_time(t);\n        primitive[0] = std::max(0., height_function_->value(point) - z);\n\n        velocity_function_->set_time(t);\n        primitive[1] = velocity_function_->value(point);\n\n        const auto view = hyperbolic_system_.template view<dim, Number>();\n        return view.from_initial_state(primitive);\n      }\n\n      auto initial_precomputations(const dealii::Point<dim> &point) ->\n          typename InitialState<Description, dim, Number>::\n              initial_precomputed_type final\n      {\n        /* Compute bathymetry: */\n        return {static_cast<Number>(geotiff_reader_.compute_height(point))};\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n      mutable GeoTIFFReader geotiff_reader_;\n\n      /* Runtime parameters: */\n\n      std::string height_expression_;\n      std::string velocity_expression_;\n\n      /* Fields for muparser support for water height and velocity: */\n\n      std::unique_ptr<dealii::FunctionParser<dim>> height_function_;\n      std::unique_ptr<dealii::FunctionParser<dim>> velocity_function_;\n    };\n  } // namespace ShallowWaterInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/initial_state_hou_test.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2023 - 2025 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\n#include <deal.II/base/function_parser.h>\n\nnamespace ryujin\n{\n  namespace ShallowWaterInitialStates\n  {\n    /**\n     * Wetting/drying test proposed in Section 3.8 of @hou2013.\n     *\n     * @ingroup ShallowWaterEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class HouTest : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n\n      HouTest(const HyperbolicSystem &hyperbolic_system, const std::string s)\n          : InitialState<Description, dim, Number>(\"hou test\", s)\n          , hyperbolic_system(hyperbolic_system)\n      {\n        depth_ = 35;\n        this->add_parameter(\"reservoir water depth\",\n                            depth_,\n                            \"Depth of water in reservoir behind dam\");\n      }\n\n      state_type compute(const dealii::Point<dim> &point, Number /*t*/) final\n      {\n        if constexpr (dim == 1) {\n          AssertThrow(false, dealii::ExcNotImplemented());\n          __builtin_trap();\n\n        } else {\n          const Number x = point[0];\n\n          const Number bath = compute_bathymetry(point);\n\n          /* Set water depth behind resevoir */\n          Number h = 0.;\n          if (x < -100.)\n            h = std::max(depth_ - bath, Number(0.));\n\n          return state_type{{h, 0.}};\n        }\n      }\n\n      auto initial_precomputations(const dealii::Point<dim> &point) ->\n          typename InitialState<Description, dim, Number>::\n              initial_precomputed_type final\n      {\n        /* Compute bathymetry: */\n        return {compute_bathymetry(point)};\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system;\n      Number depth_;\n\n      DEAL_II_ALWAYS_INLINE inline Number\n      compute_bathymetry(const dealii::Point<dim> &point) const\n      {\n        const Number x = point[0];\n        const Number y = point[1];\n\n        Number base;\n        {\n          Number base1 = std::pow(x + 250., 2) / 1600. + std::pow(y, 2) / 400.;\n          Number base2 = std::pow(x, 2) / 225. + std::pow(y - 50., 2) / 225.;\n          Number base3 =\n              std::pow(x - 250., 2) / 1225. + std::pow(y, 2) / 225. - 10.;\n\n          base = std::min(base1, base2);\n          base = std::min(base, base3);\n        }\n\n        Number bumps;\n        {\n          Number bump1 =\n              80. - std::pow(x + 250., 2) / 50. - std::pow(y, 2) / 50.;\n\n          Number bump2 = (std::pow(x - 200., 2) + std::pow(y + 10., 2) <= 1000.)\n                             ? 10.\n                             : 0.;\n          Number bump3 = (std::abs(x - 380.) <= 40. && std::abs(y - 50.) <= 40.)\n                             ? 20.\n                             : 0.;\n\n          bumps = std::max(bump1, bump2);\n          bumps = std::max(bumps, bump3);\n        }\n        return std::max(base, bumps);\n      }\n    };\n\n  } // namespace ShallowWaterInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/initial_state_library.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2023 - 2024 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#include \"initial_state_library.template.h\"\n\nnamespace ryujin\n{\n  template class InitialStateLibrary<Description, 1, NUMBER>;\n  template class InitialStateLibrary<Description, 2, NUMBER>;\n  template class InitialStateLibrary<Description, 3, NUMBER>;\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/initial_state_library.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2023 - 2025 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include \"description.h\"\n\n#include \"initial_state_library_shallow_water.h\"\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  using Description = ShallowWater::Description;\n\n  template <int dim, typename Number>\n  class InitialStateLibrary<Description, dim, Number>\n  {\n  public:\n    using HyperbolicSystem = typename Description::HyperbolicSystem;\n    using ParabolicSystem = typename Description::ParabolicSystem;\n\n    using View =\n        typename Description::template HyperbolicSystemView<dim, Number>;\n\n    using initial_state_list_type =\n        std::set<std::unique_ptr<InitialState<Description, dim, Number>>>;\n\n    static void\n    populate_initial_state_list(initial_state_list_type &initial_state_list,\n                                const HyperbolicSystem &h,\n                                const ParabolicSystem & /*p*/,\n                                const std::string &s)\n    {\n      ShallowWaterInitialStates::\n          populate_initial_state_list<Description, dim, Number>(\n              initial_state_list, h, s);\n    }\n  };\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/initial_state_library_shallow_water.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2023 - 2025 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\n#include \"initial_state_contrast.h\"\n#include \"initial_state_flow_over_bump.h\"\n#include \"initial_state_function.h\"\n#include \"initial_state_geotiff.h\"\n#include \"initial_state_hou_test.h\"\n#include \"initial_state_paraboloid.h\"\n#include \"initial_state_ritter_dam_break.h\"\n#include \"initial_state_sloping_friction.h\"\n#include \"initial_state_smooth_vortex.h\"\n#include \"initial_state_soliton.h\"\n#include \"initial_state_three_bumps_dam_break.h\"\n#include \"initial_state_transient.h\"\n#include \"initial_state_uniform.h\"\n\n\nnamespace ryujin\n{\n  namespace ShallowWaterInitialStates\n  {\n    template <typename Description, int dim, typename Number>\n    void populate_initial_state_list(\n        typename ryujin::InitialStateLibrary<Description, dim, Number>::\n            initial_state_list_type &initial_state_list,\n        const typename Description::HyperbolicSystem &h,\n        const std::string &s)\n    {\n      auto add = [&](auto &&object) {\n        initial_state_list.emplace(std::move(object));\n      };\n\n      add(std::make_unique<Contrast<Description, dim, Number>>(h, s));\n      add(std::make_unique<FlowOverBump<Description, dim, Number>>(h, s));\n      add(std::make_unique<Function<Description, dim, Number>>(h, s));\n      add(std::make_unique<GeoTIFF<Description, dim, Number>>(h, s));\n      add(std::make_unique<HouTest<Description, dim, Number>>(h, s));\n      add(std::make_unique<Paraboloid<Description, dim, Number>>(h, s));\n      add(std::make_unique<RitterDamBreak<Description, dim, Number>>(h, s));\n      add(std::make_unique<SlopingFriction<Description, dim, Number>>(h, s));\n      add(std::make_unique<SmoothVortex<Description, dim, Number>>(h, s));\n      add(std::make_unique<ThreeBumpsDamBreak<Description, dim, Number>>(h, s));\n      add(std::make_unique<Uniform<Description, dim, Number>>(h, s));\n      add(std::make_unique<TankExperiments<Description, dim, Number>>(h, s));\n      add(std::make_unique<Soliton<Description, dim, Number>>(h, s));\n    }\n  } // namespace ShallowWaterInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/initial_state_paraboloid.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2022 - 2025 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  namespace ShallowWaterInitialStates\n  {\n    /**\n     * A 1D/2D configuration for planar surface flow in a radially-symmetric\n     * paraboloid basin without friction. See Section 4.2.2 of\n     * @cite swashes_2013 for details.\n     *\n     * @note The 1D variation is a different than the reference above and has\n     * frictions effects.\n     *\n     * @todo Add variation with friction for 2D case.\n     *\n     * @ingroup ShallowWaterEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class Paraboloid : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n\n      Paraboloid(const HyperbolicSystem &hyperbolic_system,\n                 const std::string subsection)\n          : InitialState<Description, dim, Number>(\"paraboloid\", subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n        a_ = 1.;\n        this->add_parameter(\n            \"free surface radius\", a_, \"Radius of the circular free surface\");\n\n        h_0_ = 0.1;\n        this->add_parameter(\n            \"water height\", h_0_, \"Water height at central point\");\n\n        if constexpr (dim == 1) {\n\n          length_ = 10000.;\n          this->add_parameter(\n              \"paraboloid length\", length_, \"Length of 1D paraboloid\");\n\n          B_ = 2.;\n          this->add_parameter(\"speed\", B_, \"The 1D paraboloid speed\");\n\n        } else {\n          eta_ = 0.5;\n          this->add_parameter(\"eta\", eta_, \"The eta parameter\");\n        }\n      }\n\n      state_type compute(const dealii::Point<dim> &point, Number t) final\n      {\n        const auto view = hyperbolic_system_.template view<dim, Number>();\n\n        /* Common quantities */\n        const auto z = compute_bathymetry(point);\n        const auto g = view.gravity();\n        const Number omega = std::sqrt(2. * g * h_0_) / a_;\n        const Number &x = point[0];\n\n        /* Initialize primitive variables */\n        Number h, v_x, v_y = 0.;\n\n        /* Define slightly different profiles for each dimension */\n\n        if constexpr (dim == 1) {\n\n          const Number k = view.manning_friction_coefficient();\n          const Number p = std::sqrt(8. * g * h_0_) / a_;\n          const Number s = std::sqrt(p * p - k * k) / 2.;\n\n          auto term1 =\n              (a_ * a_ * B_ * B_) / (8. * g * g * h_0_) * std::exp(-k * t);\n          term1 *= (1. / 4. * k * k - s * s) * std::cos(2. * s * t) -\n                   s * k * sin(2. * s * t);\n\n          const auto term2 = -(B_ * B_ / (4. * g)) * std::exp(-k * t);\n\n          auto term3 = -(B_ / g) * std::exp(-1. / 2. * k * t);\n          term3 *= (s * std::cos(s * t) + 1. / 2. * k * std::sin(s * t)) *\n                   (point[0] - 1. / 2. * length_);\n\n          auto htilde = h_0_ - compute_bathymetry(point);\n          htilde += term1 + term2 + term3;\n\n          h = std::max(htilde, Number(0.));\n          v_x = B_ * std::exp(-1. / 2. * k * t) * std::sin(s * t);\n\n          return state_type{{h, h * v_x}};\n        } else if constexpr (dim == 2) {\n\n          const Number &y = point[1];\n\n          const Number elevation =\n              eta_ * h_0_ / (a_ * a_) *\n              (2. * x * std::cos(omega * t) + 2. * y * std::sin(omega * t));\n\n          h = std::max(elevation - z, Number(0.));\n          v_x = -eta_ * omega * std::sin(omega * t);\n          v_y = eta_ * omega * std::cos(omega * t);\n\n          return state_type{{h, h * v_x, h * v_y}};\n\n        } else {\n          AssertThrow(false, dealii::ExcNotImplemented());\n          __builtin_trap();\n        }\n      }\n\n\n      auto initial_precomputations(const dealii::Point<dim> &point) ->\n          typename InitialState<Description, dim, Number>::\n              initial_precomputed_type final\n      {\n        /* Compute bathymetry: */\n        return {compute_bathymetry(point)};\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n      DEAL_II_ALWAYS_INLINE inline Number\n      compute_bathymetry(const dealii::Point<dim> &point) const\n      {\n        if constexpr (dim == 1)\n          return h_0_ / (a_ * a_) * std::pow(point[0] - 0.5 * length_, 2);\n        else\n          return -h_0_ * (Number(1.) - point.norm_square() / (a_ * a_));\n      }\n\n      Number a_;\n      Number h_0_;\n      Number eta_;\n      Number length_;\n      Number B_;\n    };\n\n  } // namespace ShallowWaterInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/initial_state_ritter_dam_break.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2022 - 2025 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  namespace ShallowWaterInitialStates\n  {\n    /**\n     * Ritter's dam break solution. This is one-dimensional dam break without\n     * friction. See Section 7.3 in @cite GuermondEtAl2018SW for details.\n     *\n     * @ingroup ShallowWaterEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class RitterDamBreak : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n\n      RitterDamBreak(const HyperbolicSystem &hyperbolic_system,\n                     const std::string subsection)\n          : InitialState<Description, dim, Number>(\"ritter dam break\",\n                                                   subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n        dealii::ParameterAcceptor::parse_parameters_call_back.connect(\n            std::bind(&RitterDamBreak::parse_parameters_callback, this));\n\n        t_initial_ = 0.1;\n        this->add_parameter(\"time initial\",\n                            t_initial_,\n                            \"Time at which initial state is prescribed\");\n\n        left_depth = 0.005;\n        this->add_parameter(\"left water depth\",\n                            left_depth,\n                            \"Depth of water to the left of pseudo-dam (x<0)\");\n      }\n\n      void parse_parameters_callback()\n      {\n        AssertThrow(t_initial_ > 0.,\n                    dealii::ExcMessage(\"Expansion must be computed at an \"\n                                       \"initial time greater than 0.\"));\n      }\n\n      state_type compute(const dealii::Point<dim> &point, Number t) final\n      {\n        const auto view = hyperbolic_system_.template view<dim, Number>();\n        const auto g = view.gravity();\n\n        const auto x = point[0];\n\n        const Number aL = std::sqrt(g * left_depth);\n        const Number xA = -(t + t_initial_) * aL;\n        const Number xB = Number(2.) * (t + t_initial_) * aL;\n\n        const Number tmp = aL - x / (2. * (t + t_initial_));\n\n        const Number h_expansion = 4. / (9. * g) * tmp * tmp;\n        const Number v_expansion = 2. / 3. * (x / (t + t_initial_) + aL);\n\n        if (x <= xA)\n          return state_type{{left_depth, Number(0.)}};\n        else if (x <= xB)\n          return state_type{{h_expansion, h_expansion * v_expansion}};\n        else\n          return state_type{{Number(0.), Number(0.)}};\n      }\n\n      /* Default bathymetry of 0 */\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n      Number t_initial_;\n      Number left_depth;\n    };\n\n  } // namespace ShallowWaterInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/initial_state_sloping_friction.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2024 - 2025 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\n#include <simd.h>\n\nnamespace ryujin\n{\n  namespace ShallowWaterInitialStates\n  {\n    /**\n     * A 1D benchmark configuration consisting of a steady flow over an inclined\n     * plane with Manning's friction. See section 4.1 of @Chertock2015 for\n     * details.\n     *\n     * @ingroup ShallowWaterEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class SlopingFriction : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n\n      SlopingFriction(const HyperbolicSystem &hyperbolic_system,\n                      const std::string subsection)\n          : InitialState<Description, dim, Number>(\"sloping friction\",\n                                                   subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n        slope_ = 1.;\n        this->add_parameter(\n            \"ramp slope\",\n            slope_,\n            \"The (positive) slope of the inclined plane used to \"\n            \"define the bathymetry\");\n\n        q_0_ = 0.1;\n        this->add_parameter(\"initial discharge\",\n                            q_0_,\n                            \"The initial (unit) discharge in [m^2 / s]\");\n      }\n\n      state_type compute(const dealii::Point<dim> & /*point*/,\n                         Number /*t*/) final\n      {\n        const auto view = hyperbolic_system_.template view<dim, Number>();\n\n        /*\n         * Water depth profile depends on slope, discharge and Manning's\n         * coefficient. The gamma quantity in ref is fixed to 4. / 3.\n         */\n\n        const Number n = view.manning_friction_coefficient();\n        const Number exponent = 1. / (2. + 4. / 3.);\n\n        Number profile = n * n * q_0_ * q_0_ / slope_;\n        Number h = ryujin::pow(profile, exponent);\n\n        return state_type{{h, q_0_}};\n      }\n\n      auto initial_precomputations(const dealii::Point<dim> &point) ->\n          typename InitialState<Description, dim, Number>::\n              initial_precomputed_type final\n      {\n        /* Compute bathymetry: */\n        return {compute_bathymetry(point)};\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n      DEAL_II_ALWAYS_INLINE inline Number\n      compute_bathymetry(const dealii::Point<dim> &point) const\n      {\n        return -slope_ * point[0];\n      }\n\n      Number slope_;\n      Number q_0_;\n    };\n\n  } // namespace ShallowWaterInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/initial_state_smooth_vortex.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2022 - 2025 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  namespace ShallowWaterInitialStates\n  {\n    /**\n     * Smooth vortex problem with and without topography. Without topography,\n     * the smooth vortex moves through space. With topography, it is a steady\n     * vortex in space. See Section 2.3 in @cite Ricchiuto_Bollermann_2009\n     * for details.\n     *\n     * @ingroup ShallowWaterEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class SmoothVortex : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n\n      SmoothVortex(const HyperbolicSystem &hyperbolic_system,\n                   const std::string subsection)\n          : InitialState<Description, dim, Number>(\"smooth vortex\", subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n        with_bathymetry = false;\n        this->add_parameter(\"with bathymetry\",\n                            with_bathymetry,\n                            \"If set to true then the initial profile includes \"\n                            \"the bathymetry for the steady vortex. \");\n\n        depth_ = 1.0;\n        this->add_parameter(\"reference depth\", depth_, \"Reference water depth\");\n\n        mach_number_ = 2.0;\n        this->add_parameter(\n            \"mach number\", mach_number_, \"Mach number of unsteady vortex\");\n\n        beta_ = 0.1;\n        this->add_parameter(\"beta\", beta_, \"vortex strength beta\");\n      }\n\n      state_type compute(const dealii::Point<dim> &point, Number t) final\n      {\n        const auto view = hyperbolic_system_.template view<dim, Number>();\n        const auto gravity = view.gravity();\n\n        dealii::Point<2> point_bar;\n        point_bar[0] = point[0] - mach_number_ * t;\n        point_bar[1] = point[1];\n\n        const Number r_square = Number(point_bar.norm_square());\n\n        /* We assume r0 = 1 */\n        const Number factor =\n            beta_ / Number(2. * M_PI) *\n            exp(Number(0.5) - Number(0.5) * r_square /* /r0^ 2*/);\n\n        Number h =\n            depth_ - Number(1. / (2. * gravity /* * r0^2 */)) * factor * factor;\n\n        if (with_bathymetry)\n          h -= compute_bathymetry(point);\n\n        const Number u = mach_number_ - factor * Number(point_bar[1]);\n        const Number v = factor * Number(point_bar[0]);\n\n        if constexpr (dim == 2)\n          return state_type{{h, h * u, h * v}};\n\n        else {\n          AssertThrow(false, dealii::ExcNotImplemented());\n          __builtin_trap();\n        }\n      }\n\n      auto initial_precomputations(const dealii::Point<dim> &point) ->\n          typename InitialState<Description, dim, Number>::\n              initial_precomputed_type final\n      {\n        /* Compute bathymetry: */\n        return {compute_bathymetry(point)};\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n      DEAL_II_ALWAYS_INLINE inline Number\n      compute_bathymetry(const dealii::Point<dim> &point) const\n      {\n        const Number r_square = Number(point.norm_square());\n\n        /* We assume r0 = 1 */\n        const Number factor =\n            beta_ / Number(2. * M_PI) *\n            exp(Number(0.5) - Number(0.5) * r_square /* /r0^ 2*/);\n\n        Number bath = 0.;\n        if (with_bathymetry)\n          bath = depth_ / 4. * factor;\n\n        return bath;\n      }\n\n      bool with_bathymetry;\n      Number depth_;\n      Number mach_number_;\n      Number beta_;\n    };\n\n  } // namespace ShallowWaterInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/initial_state_soliton.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2024 - 2025 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  namespace ShallowWaterInitialStates\n  {\n    /**\n     * Solitary wave over flat bottom.\n     *\n     * @ingroup ShallowWaterEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class Soliton : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n\n      Soliton(const HyperbolicSystem &hyperbolic_system,\n              const std::string subsection)\n          : InitialState<Description, dim, Number>(\"soliton\", subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n        depth_ = 1;\n        this->add_parameter(\n            \"still water depth\", depth_, \"Depth of still water\");\n\n        amplitude_ = 0.1;\n        this->add_parameter(\"amplitude\", amplitude_, \"Amplitude of soliton\");\n      }\n\n      state_type compute(const dealii::Point<dim> &point, Number t) final\n      {\n        const auto view = hyperbolic_system_.template view<dim, Number>();\n        const Number g = view.gravity();\n\n        const auto &x = point[0];\n\n        const Number celerity = std::sqrt(g * (amplitude_ + depth_));\n        const Number width = std::sqrt(\n            3. * amplitude_ / (4. * depth_ * depth_ * (amplitude_ + depth_)));\n        const Number sechSqd =\n            1. / std::pow(cosh(width * (x - celerity * t)), 2);\n\n        /* If there is bathymetry, take max of profile and 0 */\n        const Number profile = depth_ + amplitude_ * sechSqd;\n        const Number h = std::max(profile, Number(0.));\n\n        const Number v = celerity * (profile - depth_) / profile;\n\n        return state_type{{h, h * v}};\n      }\n\n      /* Default bathymetry of 0 */\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n      Number depth_;\n      Number amplitude_;\n    };\n\n  } // namespace ShallowWaterInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/initial_state_three_bumps_dam_break.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2022 - 2025 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  namespace ShallowWaterInitialStates\n  {\n    /**\n     * Dam break with three conical islands as obstacles.\n     * See Section 7.8 in @cite GuermondEtAl2018SW for details.\n     *\n     * @ingroup ShallowWaterEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class ThreeBumpsDamBreak : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n\n      ThreeBumpsDamBreak(const HyperbolicSystem &hyperbolic_system,\n                         const std::string subsection)\n          : InitialState<Description, dim, Number>(\"three bumps dam break\",\n                                                   subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n        well_balancing_validation = false;\n        this->add_parameter(\n            \"well balancing validation\",\n            well_balancing_validation,\n            \"If set to true then the initial profile is returned for all \"\n            \"times \"\n            \"(t>0); otherwise a constant inflow is computed for t>0 suitable \"\n            \"for prescribing Dirichlet conditions at the inflow boundary.\");\n\n\n        left_depth = 1.875;\n        this->add_parameter(\"left water depth\",\n                            left_depth,\n                            \"Depth of water to the left of pseudo-dam\");\n        right_depth = 0.;\n        this->add_parameter(\"right water depth\",\n                            right_depth,\n                            \"Depth of water to the right of pseudo-dam\");\n\n        cone_magnitude = 1.;\n        this->add_parameter(\"cone magnitude\",\n                            cone_magnitude,\n                            \"To modify magnitude of cone heights\");\n      }\n\n      state_type compute(const dealii::Point<dim> &point, Number t) final\n      {\n        const auto view = hyperbolic_system_.template view<dim, Number>();\n\n        const Number x = point[0];\n\n        /* Initial state: */\n\n        if (t <= 1.e-10 || well_balancing_validation) {\n          Number h = x < 16. ? left_depth : right_depth;\n          h = std::max(h - compute_bathymetry(point), Number(0.));\n          return state_type{{h, 0.}};\n        }\n\n        /* For t > 0 prescribe constant inflow Dirichlet data on the left: */\n\n        const auto &h = left_depth;\n        const auto a = view.speed_of_sound(state_type{{h, Number(0.)}});\n        return state_type{{h, h * a}};\n      }\n\n      auto initial_precomputations(const dealii::Point<dim> &point) ->\n          typename InitialState<Description, dim, Number>::\n              initial_precomputed_type final\n      {\n        /* Compute bathymetry: */\n        return {compute_bathymetry(point)};\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n      DEAL_II_ALWAYS_INLINE inline Number\n      compute_bathymetry(const dealii::Point<dim> &point) const\n      {\n        if constexpr (dim == 1) {\n          /* When dim = 1, we only have one cone */\n          const Number &x = point[0];\n\n          Number z3 = 3. - 3. / 10. * std::sqrt(std::pow(x - 47.5, 2));\n          return cone_magnitude * std::max({z3, Number(0.)});\n        }\n\n        const Number &x = point[0];\n        const Number &y = point[1];\n\n        Number z1 =\n            1. -\n            1. / 8. * std::sqrt(std::pow(x - 30., 2) + std::pow(y - 6., 2));\n\n        Number z2 =\n            1. -\n            1. / 8. * std::sqrt(std::pow(x - 30., 2) + std::pow(y - 24., 2));\n\n        Number z3 =\n            3. -\n            3. / 10. * std::sqrt(std::pow(x - 47.5, 2) + std::pow(y - 15., 2));\n\n        return cone_magnitude * std::max({z1, z2, z3, Number(0.)});\n      }\n\n      bool well_balancing_validation;\n      Number left_depth;\n      Number right_depth;\n      Number cone_magnitude;\n    };\n\n  } // namespace ShallowWaterInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/initial_state_transient.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2024 - 2025 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  namespace ShallowWaterInitialStates\n  {\n    /**\n     * Various experiment configurations described in @cite Martinez2018.\n     *\n     * @ingroup ShallowWaterEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class TankExperiments : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n\n      TankExperiments(const HyperbolicSystem &hyperbolic_system,\n                      const std::string subsection)\n          : InitialState<Description, dim, Number>(\"transient experiments\",\n                                                   subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n        dealii::ParameterAcceptor::parse_parameters_call_back.connect(\n            std::bind(&TankExperiments::parse_parameters_callback, this));\n\n        state_left_[0] = 1.;\n        state_left_[1] = 0.0;\n        this->add_parameter(\"flow state left\",\n                            state_left_,\n                            \"Initial 1d flow state (h, q) on the left\");\n\n        state_right_[0] = 1.;\n        state_right_[1] = 0.0;\n        this->add_parameter(\"flow state right\",\n                            state_right_,\n                            \"Initial 1d flow state (h, q) on the right\");\n\n        which_case_ = \"G1\";\n        this->add_parameter(\"experimental configuration\",\n                            which_case_,\n                            \"Either 'G1', 'G2', 'G3' or 'none' for bathymetry \"\n                            \"configuration\");\n      }\n\n      void parse_parameters_callback()\n      {\n        AssertThrow(\n            which_case_ == \"G1\" || which_case_ == \"G2\" || which_case_ == \"G3\" ||\n                which_case_ == \"none\",\n            dealii::ExcMessage(\"Case must be 'G1', 'G2', 'G3' or 'none'\"));\n      }\n\n      state_type compute(const dealii::Point<dim> &point, Number /* t */) final\n      {\n        const auto view = hyperbolic_system_.template view<dim, Number>();\n\n        if constexpr (dim == 1) {\n          AssertThrow(false, dealii::ExcNotImplemented());\n          __builtin_trap();\n        }\n\n        const auto temp = point[0] > 1.e-8 ? state_right_ : state_left_;\n        return view.expand_state(temp);\n      }\n\n      auto initial_precomputations(const dealii::Point<dim> &point) ->\n          typename InitialState<Description, dim, Number>::\n              initial_precomputed_type final\n      {\n        /* Compute bathymetry: */\n        return {compute_bathymetry(point)};\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n      DEAL_II_ALWAYS_INLINE\n      inline Number compute_bathymetry(const dealii::Point<dim> &point) const\n      {\n        const auto &x = point[0], &y = point[1];\n\n        /* Bathymetry base is the same for all configurations */\n        Number bath = 0.;\n        if (x >= 0. && x <= 326. / 100.)\n          bath = -0.00092 * x;\n        else if (x > 326. / 100.)\n          bath = -0.0404 * (x - 326. / 100.) - 0.00092 * 326. / 100.;\n\n        if (which_case_ == \"none\")\n          return bath;\n\n        /* Initialize obstacle to 0 */\n        Number obstacle = 0.;\n\n\n        // G1 -- rectangular obstacle\n        if (which_case_ == \"G1\") {\n\n          Number obstacle_length = 16.3 / 100.;\n          Number obstacle_width = 8. / 100.;\n\n          Number xc = 205. / 100. + (16.3 / 2. / 100.); // obstacle center\n\n          if (std::abs((x - xc) / obstacle_length + y / obstacle_width) +\n                  std::abs((x - xc) / obstacle_length - y / obstacle_width) <=\n              1.)\n            obstacle = 7. / 100.;\n        } else if (which_case_ == \"G2\") { // circular bump + rectangle\n\n          // circular bump\n          double xc = 184.5 / 100. + 31. / 2. / 100.;\n          const double radicand =\n              positive_part(1. - std::pow((x - xc) / (31. / 2. / 100.), 2));\n\n          const double semi_circle = 7.3 / 100. * std::sqrt(radicand);\n\n          obstacle = std::max(semi_circle, 0.);\n\n          // rectangular obstacle\n          double obstacle_length = 16.3 / 100.;\n          double obstacle_width = 8. / 100.;\n\n          xc = 235. / 100. + (16.3 / 2. / 100.); // obstacle center\n\n          if (std::abs((x - xc) / obstacle_length + y / obstacle_width) +\n                  std::abs((x - xc) / obstacle_length - y / obstacle_width) <=\n              1.)\n            obstacle = 7. / 100.;\n        } else if (which_case_ == \"G3\") { // narrowing half-circle + rectangle\n\n          // narrowing half-circles canal\n          double xc = 194 / 100. + 31. / 2. / 100.;\n          const double radicand =\n              positive_part(1. - std::pow((x - xc) / (31. / 2. / 100.), 2));\n          const double semi_circle = 7.3 / 100. * std::sqrt(radicand);\n\n          if (y < semi_circle - 24. / 2. / 100. &&\n              std::abs(x - xc) <= 31. / 2. / 100.)\n            obstacle = 21. / 100.;\n\n          if (y > -semi_circle + 24. / 2. / 100. &&\n              std::abs(x - xc) <= 31. / 2. / 100.)\n            obstacle = 21. / 100.;\n\n          // rectangular obstacle\n          double obstacle_length = 16.3 / 100.;\n          double obstacle_width = 8. / 100.;\n\n          xc = 235. / 100. + (16.3 / 2. / 100.); // obstacle center\n\n          if (std::abs((x - xc) / obstacle_length + y / obstacle_width) +\n                  std::abs((x - xc) / obstacle_length - y / obstacle_width) <=\n              1.)\n            obstacle = 7. / 100.;\n        }\n\n        return bath + obstacle;\n      }\n\n      dealii::Tensor<1, 2, Number> state_left_;\n      dealii::Tensor<1, 2, Number> state_right_;\n\n      std::string which_case_;\n      std::string flow_type_;\n    };\n\n  } // namespace ShallowWaterInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/initial_state_uniform.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2022 - 2025 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  namespace ShallowWaterInitialStates\n  {\n    /**\n     * Returns a uniform initial state defined by a given primitive\n     * (initial) state.\n     *\n     * @note The @p t argument is ignored. This class always returns the\n     * initial configuration.\n     *\n     * @ingroup ShallowWaterEquations\n     */\n    template <typename Description, int dim, typename Number>\n    class Uniform : public InitialState<Description, dim, Number>\n    {\n    public:\n      using HyperbolicSystem = typename Description::HyperbolicSystem;\n      using View =\n          typename Description::template HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n\n      Uniform(const HyperbolicSystem &hyperbolic_system,\n              const std::string subsection)\n          : InitialState<Description, dim, Number>(\"uniform\", subsection)\n          , hyperbolic_system_(hyperbolic_system)\n      {\n        primitive_[0] = 1.;\n        primitive_[1] = 1.;\n        this->add_parameter(\n            \"primitive state\", primitive_, \"Initial 1d primitive state (h, u)\");\n      }\n\n      state_type compute(const dealii::Point<dim> & /*point*/,\n                         Number /*t*/) final\n      {\n        const auto view = hyperbolic_system_.template view<dim, Number>();\n        return view.from_initial_state(primitive_);\n      }\n\n      /* Default bathymetry of 0 */\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n      dealii::Tensor<1, 2, Number> primitive_;\n    };\n\n  } // namespace ShallowWaterInitialStates\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/instantiate.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2023 - 2024 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#ifndef RYUJIN_INCLUDE_INSTANTIATION_ONCE\n#define RYUJIN_INCLUDE_INSTANTIATION_ONCE\n#else\n#error Instantiation files can only be included once.\n#endif\n\n#include \"description.h\"\n\nnamespace ryujin\n{\n  using ShallowWater::Description;\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/limiter.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2020 - 2024 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#include \"limiter.template.h\"\n\nusing namespace dealii;\n\nnamespace ryujin\n{\n  namespace ShallowWater\n  {\n    /* instantiations */\n\n    template class Limiter<1, NUMBER>;\n    template class Limiter<2, NUMBER>;\n    template class Limiter<3, NUMBER>;\n\n    template class Limiter<1, dealii::VectorizedArray<NUMBER>>;\n    template class Limiter<2, dealii::VectorizedArray<NUMBER>>;\n    template class Limiter<3, dealii::VectorizedArray<NUMBER>>;\n  } // namespace ShallowWater\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/limiter.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2023 - 2025 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"hyperbolic_system.h\"\n\n#include <multicomponent_vector.h>\n#include <newton.h>\n\nnamespace ryujin\n{\n  namespace ShallowWater\n  {\n    template <typename ScalarNumber = double>\n    class LimiterParameters : public dealii::ParameterAcceptor\n    {\n    public:\n      LimiterParameters(const std::string &subsection = \"/Limiter\")\n          : ParameterAcceptor(subsection)\n      {\n        iterations_ = 2;\n        add_parameter(\n            \"iterations\", iterations_, \"Number of limiter iterations\");\n\n        if constexpr (std::is_same_v<ScalarNumber, double>)\n          newton_tolerance_ = 1.e-10;\n        else\n          newton_tolerance_ = 1.e-4;\n        add_parameter(\"newton tolerance\",\n                      newton_tolerance_,\n                      \"Tolerance for the quadratic newton stopping criterion\");\n\n        newton_max_iterations_ = 2;\n        add_parameter(\"newton max iterations\",\n                      newton_max_iterations_,\n                      \"Maximal number of quadratic newton iterations performed \"\n                      \"during limiting\");\n\n        relaxation_factor_ = ScalarNumber(1.);\n        add_parameter(\"relaxation factor\",\n                      relaxation_factor_,\n                      \"Factor for scaling the relaxation window with r_i = \"\n                      \"factor * (m_i/|Omega|)^(1.5/d).\");\n      }\n\n      ACCESSOR_READ_ONLY(iterations);\n      ACCESSOR_READ_ONLY(newton_tolerance);\n      ACCESSOR_READ_ONLY(newton_max_iterations);\n      ACCESSOR_READ_ONLY(relaxation_factor);\n\n    private:\n      unsigned int iterations_;\n      ScalarNumber newton_tolerance_;\n      unsigned int newton_max_iterations_;\n      ScalarNumber relaxation_factor_;\n    };\n\n\n    /**\n     * The convex limiter.\n     *\n     * @ingroup ShallowWaterEquations\n     */\n    template <int dim, typename Number = double>\n    class Limiter\n    {\n    public:\n      /**\n       * @name Typedefs and constexpr constants\n       */\n      //@{\n\n      using View = HyperbolicSystemView<dim, Number>;\n\n      using ScalarNumber = typename View::ScalarNumber;\n\n      static constexpr auto problem_dimension = View::problem_dimension;\n\n      using state_type = typename View::state_type;\n\n      using flux_contribution_type = typename View::flux_contribution_type;\n\n      using precomputed_type = typename View::precomputed_type;\n\n      using PrecomputedVector = typename View::PrecomputedVector;\n\n      using Parameters = LimiterParameters<ScalarNumber>;\n\n      //@}\n      /**\n       * @name Computation and manipulation of bounds\n       */\n      //@{\n      /**\n       * The number of stored entries in the bounds array.\n       */\n      static constexpr unsigned int n_bounds = 3;\n\n      /**\n       * Array type used to store accumulated bounds.\n       */\n      using Bounds = std::array<Number, n_bounds>;\n\n      /**\n       * Constructor taking a HyperbolicSystem instance as argument\n       */\n      Limiter(const HyperbolicSystem &hyperbolic_system,\n              const Parameters &parameters,\n              const PrecomputedVector &precomputed_values)\n          : hyperbolic_system(hyperbolic_system)\n          , parameters(parameters)\n          , precomputed_values(precomputed_values)\n      {\n      }\n\n      /**\n       * Given a state @p U_i and an index @p i return \"strict\" bounds,\n       * i.e., a minimal convex set containing the state.\n       */\n      Bounds projection_bounds_from_state(const unsigned int i,\n                                          const state_type &U_i) const;\n\n      /**\n       * Given two bounds bounds_left, bounds_right, this function computes\n       * a larger, combined set of bounds that this is a (convex) superset\n       * of the two.\n       */\n      Bounds combine_bounds(const Bounds &bounds_left,\n                            const Bounds &bounds_right) const;\n\n      /**\n       * This function applies a relaxation to a given a (strict) bound @p\n       * bounds using a non dimensionalized measure @p hd (that should\n       * scale as $h^d$, where $h$ is the local mesh size). This is done\n       * for the case of the shallow water equations by multiplying maximum\n       * bounds with $(1+r)$ and minimum bounds with $(1-r)$.\n       */\n      Bounds fully_relax_bounds(const Bounds &bounds, const Number &hd) const;\n\n      //@}\n      /**\n       * @name Stencil-based computation of bounds\n       *\n       * Intended usage:\n       * ```\n       * Limiter<dim, Number> limiter;\n       * for (unsigned int i = n_internal; i < n_owned; ++i) {\n       *   // ...\n       *   limiter.reset(i, U_i, flux_i);\n       *   for (unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {\n       *     // ...\n       *     limiter.accumulate(js, U_j, flux_j, scaled_c_ij, affine_shift);\n       *   }\n       *   limiter.bounds(hd_i);\n       * }\n       * ```\n       */\n      //@{\n\n      /**\n       * Reset temporary storage\n       */\n      void reset(const unsigned int i,\n                 const state_type &U_i,\n                 const flux_contribution_type &flux_i);\n\n      /**\n       * When looping over the sparsity row, add the contribution associated\n       * with the neighboring state U_j.\n       */\n      void accumulate(const state_type &U_j,\n                      const state_type &U_star_ij,\n                      const state_type &U_star_ji,\n                      const dealii::Tensor<1, dim, Number> &scaled_c_ij,\n                      const state_type &affine_shift);\n\n      /**\n       * Return the computed bounds (with relaxation applied).\n       */\n      Bounds bounds(const Number hd_i) const;\n\n      //*}\n      /** @name Convex limiter */\n      //@{\n\n      /**\n       * Given a state \\f$\\mathbf U\\f$ and an update \\f$\\mathbf P\\f$ this\n       * function computes and returns the maximal coefficient \\f$t\\f$,\n       * obeying \\f$t_{\\text{min}} < t < t_{\\text{max}}\\f$, such that the\n       * selected local minimum principles are obeyed.\n       */\n      std::tuple<Number, bool> limit(const Bounds &bounds,\n                                     const state_type &U,\n                                     const state_type &P,\n                                     const Number t_min = Number(0.),\n                                     const Number t_max = Number(1.)) const;\n\n    private:\n      //@}\n      /** @name Arguments and internal fields */\n      //@{\n\n      const HyperbolicSystem &hyperbolic_system;\n      const Parameters &parameters;\n      const PrecomputedVector &precomputed_values;\n\n      state_type U_i;\n\n      Bounds bounds_;\n\n      /* for relaxation */\n\n      Number h_relaxation_numerator;\n      Number v2_relaxation_numerator;\n      Number relaxation_denominator;\n\n      //@}\n    };\n\n\n    /*\n     * -------------------------------------------------------------------------\n     * Inline definitions\n     * -------------------------------------------------------------------------\n     */\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    Limiter<dim, Number>::projection_bounds_from_state(\n        const unsigned int /*i*/, const state_type &U_i) const -> Bounds\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n      const auto h_i = view.water_depth(U_i);\n      const auto v_i =\n          view.momentum(U_i) * view.inverse_water_depth_mollified(U_i);\n      const auto v2_i = v_i.norm_square();\n\n      return {/*h_min*/ h_i, /*h_max*/ h_i, /*v2_max*/ v2_i};\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    Limiter<dim, Number>::combine_bounds(const Bounds &bounds_l,\n                                         const Bounds &bounds_r) const -> Bounds\n    {\n      const auto &[h_min_l, h_max_l, v2_max_l] = bounds_l;\n      const auto &[h_min_r, h_max_r, v2_max_r] = bounds_r;\n\n      return {std::min(h_min_l, h_min_r),\n              std::max(h_max_l, h_max_r),\n              std::max(v2_max_l, v2_max_r)};\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    Limiter<dim, Number>::fully_relax_bounds(const Bounds &bounds,\n                                             const Number &hd) const -> Bounds\n    {\n      auto relaxed_bounds = bounds;\n      auto &[h_min, h_max, v2_max] = relaxed_bounds;\n\n      /* Use r = factor * (m_i / |Omega|) ^ (1.5 / d): */\n\n      Number r = std::sqrt(hd);                              // in 3D: ^ 3/6\n      if constexpr (dim == 2)                                //\n        r = dealii::Utilities::fixed_power<3>(std::sqrt(r)); // in 2D: ^ 3/4\n      else if constexpr (dim == 1)                           //\n        r = dealii::Utilities::fixed_power<3>(r);            // in 1D: ^ 3/2\n      r *= parameters.relaxation_factor();\n\n      constexpr ScalarNumber eps = std::numeric_limits<ScalarNumber>::epsilon();\n      h_min *= std::max((Number(1.) - r), Number(eps));\n      h_max *= (Number(1.) + r);\n      v2_max *= (Number(1.) + r);\n\n      return relaxed_bounds;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline void\n    Limiter<dim, Number>::reset(unsigned int /*i*/,\n                                const state_type &new_U_i,\n                                const flux_contribution_type & /*new_flux_i*/)\n    {\n      U_i = new_U_i;\n\n      auto &[h_min, h_max, v2_max] = bounds_;\n\n      h_min = Number(std::numeric_limits<ScalarNumber>::max());\n      h_max = Number(0.);\n      v2_max = Number(0.);\n\n      h_relaxation_numerator = Number(0.);\n      v2_relaxation_numerator = Number(0.);\n      relaxation_denominator = Number(0.);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline void Limiter<dim, Number>::accumulate(\n        const state_type &U_j,\n        const state_type &U_star_ij,\n        const state_type &U_star_ji,\n        const dealii::Tensor<1, dim, Number> &scaled_c_ij,\n        const state_type &affine_shift)\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      /* The bar states: */\n\n      const auto f_star_ij = view.f(U_star_ij);\n      const auto f_star_ji = view.f(U_star_ji);\n\n      /* bar state shifted by an affine shift: */\n      const auto U_ij_bar =\n          ScalarNumber(0.5) *\n              (U_star_ij + U_star_ji +\n               contract(add(f_star_ij, -f_star_ji), scaled_c_ij)) +\n          affine_shift;\n\n      /* Bounds: */\n\n      auto &[h_min, h_max, v2_max] = bounds_;\n\n      const auto h_bar_ij = view.water_depth(U_ij_bar);\n      h_min = std::min(h_min, h_bar_ij);\n      h_max = std::max(h_max, h_bar_ij);\n\n      const auto v_bar_ij = view.momentum(U_ij_bar) *\n                            view.inverse_water_depth_mollified(U_ij_bar);\n      const auto v2_bar_ij = v_bar_ij.norm_square();\n      v2_max = std::max(v2_max, v2_bar_ij);\n\n      /* Relaxation: */\n\n      /* Use a uniform weight. */\n      const auto beta_ij = Number(1.);\n\n      relaxation_denominator += std::abs(beta_ij);\n\n      const auto h_i = view.water_depth(U_i);\n      const auto h_j = view.water_depth(U_j);\n      h_relaxation_numerator += beta_ij * (h_i + h_j);\n\n      const auto vel_i =\n          view.momentum(U_i) * view.inverse_water_depth_mollified(U_i);\n      const auto vel_j =\n          view.momentum(U_j) * view.inverse_water_depth_mollified(U_j);\n      v2_relaxation_numerator +=\n          beta_ij * (-vel_i.norm_square() + vel_j.norm_square());\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    Limiter<dim, Number>::bounds(const Number hd_i) const -> Bounds\n    {\n      const auto &[h_min, h_max, v2_max] = bounds_;\n\n      auto relaxed_bounds = fully_relax_bounds(bounds_, hd_i);\n      auto &[h_min_relaxed, h_max_relaxed, v2_max_relaxed] = relaxed_bounds;\n\n      /* Apply a stricter window: */\n\n      constexpr ScalarNumber eps = std::numeric_limits<ScalarNumber>::epsilon();\n\n      const Number h_relaxed =\n          ScalarNumber(2. * parameters.relaxation_factor()) *\n          std::abs(h_relaxation_numerator) /\n          (relaxation_denominator + Number(eps));\n\n      const Number v2_relaxed =\n          ScalarNumber(2. * parameters.relaxation_factor()) *\n          std::abs(v2_relaxation_numerator) /\n          (relaxation_denominator + Number(eps));\n\n      h_min_relaxed = std::max(h_min_relaxed, h_min - h_relaxed);\n      h_max_relaxed = std::min(h_max_relaxed, h_max + h_relaxed);\n      v2_max_relaxed = std::min(v2_max_relaxed, v2_max + v2_relaxed);\n\n      return relaxed_bounds;\n    }\n  } // namespace ShallowWater\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/limiter.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2022 - 2025 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include \"limiter.h\"\n\nnamespace ryujin\n{\n  namespace ShallowWater\n  {\n    template <int dim, typename Number>\n    std::tuple<Number, bool>\n    Limiter<dim, Number>::limit(const Bounds &bounds,\n                                const state_type &U,\n                                const state_type &P,\n                                const Number t_min /* = Number(0.) */,\n                                const Number t_max /* = Number(1.) */) const\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      bool success = true;\n      Number t_l = t_min;\n      Number t_r = t_max;\n\n      const auto &[h_min, h_max, v2_max] = bounds;\n\n      constexpr ScalarNumber min = std::numeric_limits<ScalarNumber>::min();\n      constexpr ScalarNumber eps = std::numeric_limits<ScalarNumber>::epsilon();\n      const auto small = view.dry_state_relaxation_small();\n      const auto large = view.dry_state_relaxation_large();\n      const auto relax_small = ScalarNumber(1. + small * eps);\n      const auto relax = ScalarNumber(1. + large * eps);\n\n      /*\n       * We first limit the water_depth h.\n       *\n       * See [Guermond et al, 2021] (5.7).\n       */\n\n      {\n        auto h_U = view.water_depth(U);\n        const auto &h_P = view.water_depth(P);\n\n        const auto test_min = view.filter_dry_water_depth(\n            std::max(Number(0.), h_U - relax * h_max));\n        const auto test_max = view.filter_dry_water_depth(\n            std::max(Number(0.), h_min - relax * h_U));\n\n        if (!(test_min == Number(0.) && test_max == Number(0.))) {\n#ifdef DEBUG_OUTPUT\n          std::cout << std::fixed << std::setprecision(16);\n          std::cout << \"Bounds violation: low-order water depth (critical)!\\n\"\n                    << \"\\n\\t\\th min:         \" << h_min\n                    << \"\\n\\t\\th min (delta): \" << negative_part(h_U - h_min)\n                    << \"\\n\\t\\th:             \" << h_U\n                    << \"\\n\\t\\th max (delta): \" << positive_part(h_U - h_max)\n                    << \"\\n\\t\\th max:         \" << h_max << \"\\n\"\n                    << std::endl;\n#endif\n          success = false;\n        }\n\n        const Number denominator =\n            ScalarNumber(1.) / (std::abs(h_P) + eps * h_max + min);\n\n        constexpr auto lt = dealii::SIMDComparison::less_than;\n\n        t_r = dealii::compare_and_apply_mask<lt>( //\n            h_max,\n            h_U + t_r * h_P,\n            /*\n             * h_P is positive.\n             *\n             * Note: Do not take an absolute value here. If we are out of\n             * bounds we have to ensure that t_r is set to t_min.\n             */\n            (h_max - h_U) * denominator,\n            t_r);\n\n        t_r = dealii::compare_and_apply_mask<lt>( //\n            h_U + t_r * h_P,\n            h_min,\n            /*\n             * h_P is negative.\n             *\n             * Note: Do not take an absolute value here. If we are out of\n             * bounds we have to ensure that t_r is set to t_min.\n             */\n            (h_U - h_min) * denominator,\n            t_r);\n\n        /*\n         * Ensure that t_min <= t <= t_max. This might not be the case if\n         * h_U is outside the interval [h_min, h_max]. Furthermore, the\n         * quotient we take above is prone to numerical cancellation in\n         * particular in the second pass of the limiter when h_P might be\n         * small.\n         */\n        t_r = std::min(t_r, t_max);\n        t_r = std::max(t_r, t_min);\n\n\n#ifdef DEBUG_EXPENSIVE_BOUNDS_CHECK\n        /*\n         * Verify that the new state is within bounds:\n         */\n        const auto h_new = view.water_depth(U + t_r * P);\n        const auto test_new_min = view.filter_dry_water_depth(\n            std::max(Number(0.), h_new - relax * h_max));\n        const auto test_new_max = view.filter_dry_water_depth(\n            std::max(Number(0.), h_min - relax * h_new));\n\n        if (!(test_new_min == Number(0.) && test_new_max == Number(0.))) {\n#ifdef DEBUG_OUTPUT\n          std::cout << std::fixed << std::setprecision(30);\n          std::cout << \"Bounds violation: high-order water depth!\\n\"\n                    << \"\\n\\t\\th min:         \" << h_min\n                    << \"\\n\\t\\th min (delta): \" << negative_part(h_new - h_min)\n                    << \"\\n\\t\\th:             \" << h_new\n                    << \"\\n\\t\\th max (delta): \" << positive_part(h_new - h_max)\n                    << \"\\n\\t\\th max:         \" << h_max << \"\\n\"\n                    << std::endl;\n#endif\n          success = false;\n        }\n#endif\n      }\n\n      /*\n       * Limit the (negative) |v|^2:\n       *\n       * Given initial limiter values t_l and t_r with psi(t_l) > 0 and\n       * psi(t_r) < 0 we try to find t^\\ast with psi(t^\\ast) \\approx 0.\n       *\n       * Here, psi is the function:\n       *\n       *   psi = h^2 (|v|^2)^max - |q|^2\n       */\n\n      {\n        /* We first check if t_r is a good state */\n\n        const auto U_r = U + t_r * P;\n        const auto h_r = view.water_depth(U_r);\n        const auto q_r = view.momentum(U_r);\n\n        const auto psi_r = relax_small * h_r * h_r * v2_max - q_r.norm_square();\n\n        /*\n         * If psi_r > 0 the right state is fine, force returning t_r by\n         * setting t_l = t_r:\n         */\n        t_l = dealii::compare_and_apply_mask<\n            dealii::SIMDComparison::greater_than>(psi_r, Number(0.), t_r, t_l);\n\n        /* If we have set t_l = t_r everywhere we can return: */\n        if (t_l == t_r)\n          return {t_l, success};\n\n#ifdef DEBUG_OUTPUT_LIMITER\n        {\n          std::cout << std::endl;\n          std::cout << std::fixed << std::setprecision(16);\n          std::cout << \"t_l: (start) \" << t_l << std::endl;\n          std::cout << \"t_r: (start) \" << t_r << std::endl;\n        }\n#endif\n\n        const auto U_l = U + t_l * P;\n        const auto h_l = view.water_depth(U_l);\n        const auto q_l = view.momentum(U_l);\n\n        const auto psi_l = relax_small * h_l * h_l * v2_max - q_l.norm_square();\n\n        /*\n         * Verify that the left state is within bounds. This property might\n         * be violated for relative CFL numbers larger than 1.\n         *\n         * We use a non-scaled eps here to force the lower_bound to be\n         * negative so that we do not accidentally trigger in \"perfect\" dry\n         * states with h_l equal to zero.\n         */\n        const auto filtered_h_l = view.filter_dry_water_depth(h_l);\n        const auto lower_bound =\n            (ScalarNumber(1.) - relax) * filtered_h_l * filtered_h_l * v2_max -\n            ScalarNumber(100.) * eps;\n        if (!(std::min(Number(0.), psi_l - lower_bound) == Number(0.))) {\n#ifdef DEBUG_OUTPUT\n          std::cout << std::fixed << std::setprecision(16);\n          std::cout\n              << \"Bounds violation: low-order square velocity (critical)!\\n\";\n          std::cout << \"\\t\\tPsi left: 0 <= \" << psi_l << \"\\n\" << std::endl;\n#endif\n          success = false;\n        }\n\n        /*\n         * Skip the quadratic Newton step if the window between t_l and t_r\n         * is within the prescribed tolerance:\n         */\n        const Number tolerance(parameters.newton_tolerance());\n        if (!(std::max(Number(0.), t_r - t_l - tolerance) == Number(0.))) {\n          /*\n           * If the bound is not satisfied, we need to find the root of a\n           * quadratic function:\n           *\n           * psi(t)   = (h_U + t h_P)^2 v2_max\n           *            - (|q_U|^2 + 2(q_U * q_P) t + |q_P|^2 t^2)\n           *\n           * d_psi(t) = 2 (h_U + t * h_P) * h_P v2_max\n           *            - 2 (q_U * q_P) - |q_P|^2 t\n           *\n           * We can compute the root of this function efficiently by using our\n           * standard quadratic_newton_step() function that will use the points\n           * [p1, p1, p2] as well as [p1, p2, p2] to construct two quadratic\n           * polynomials to compute new candiates for the bounds [t_l, t_r]. In\n           * case of a quadratic function psi(t) both polynomials will coincide\n           * so that (up to round-off error) t_l = t_r.\n           */\n          const auto &h_U = view.water_depth(U);\n          const auto &h_P = view.water_depth(P);\n          const auto &q_U = view.momentum(U);\n          const auto &q_P = view.momentum(P);\n\n          const auto dpsi_l =\n              (h_U + t_l * h_P) * h_P * v2_max -\n              ScalarNumber(2.) * ((q_U * q_P) - q_P * q_P * t_l);\n          const auto dpsi_r =\n              (h_U + t_r * h_P) * h_P * v2_max -\n              ScalarNumber(2.) * ((q_U * q_P) - q_P * q_P * t_r);\n\n          quadratic_newton_step(\n              t_l, t_r, psi_l, psi_r, dpsi_l, dpsi_r, Number(-1.));\n\n#ifdef DEBUG_OUTPUT_LIMITER\n          if (std::max(Number(0.), psi_r + Number(eps)) == Number(0.)) {\n            std::cout << \"psi_l:       \" << psi_l << std::endl;\n            std::cout << \"psi_r:       \" << psi_r << std::endl;\n            std::cout << \"dpsi_l:      \" << dpsi_l << std::endl;\n            std::cout << \"dpsi_r:      \" << dpsi_r << std::endl;\n            std::cout << \"t_l: (end)   \" << t_l << std::endl;\n            std::cout << \"t_r: (end)   \" << t_r << std::endl;\n          }\n#endif\n        }\n\n#ifdef DEBUG_EXPENSIVE_BOUNDS_CHECK\n        /*\n         * Verify that the new state is within bounds:\n         */\n        {\n          const auto U_new = U + t_l * P;\n          const auto h_new = view.water_depth(U_new);\n          const auto q_new = view.momentum(U_new);\n\n          const auto psi_new =\n              relax_small * h_new * h_new * v2_max - q_new.norm_square();\n\n          const auto lower_bound =\n              (ScalarNumber(1.) - relax) * h_new * h_new * v2_max -\n              ScalarNumber(100.) * eps;\n\n          const bool psi_valid =\n              std::min(Number(0.), psi_new - lower_bound) == Number(0.);\n          if (!psi_valid) {\n#ifdef DEBUG_OUTPUT\n            std::cout << std::fixed << std::setprecision(16);\n            std::cout << \"Bounds violation: high-order square velocity!\\n\";\n            std::cout << \"\\t\\tPsi: 0 <= \" << psi_new << \"\\n\" << std::endl;\n#endif\n            success = false;\n          }\n        }\n#endif\n      }\n\n      return {t_l, success};\n    }\n\n  } // namespace ShallowWater\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/riemann_solver.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2020 - 2024 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#include \"riemann_solver.template.h\"\n\n#include <deal.II/base/vectorization.h>\n\nnamespace ryujin\n{\n  namespace ShallowWater\n  {\n    /* instantiations */\n\n    template class RiemannSolver<1, NUMBER>;\n    template class RiemannSolver<2, NUMBER>;\n    template class RiemannSolver<3, NUMBER>;\n\n    template class RiemannSolver<1, dealii::VectorizedArray<NUMBER>>;\n    template class RiemannSolver<2, dealii::VectorizedArray<NUMBER>>;\n    template class RiemannSolver<3, dealii::VectorizedArray<NUMBER>>;\n  } // namespace ShallowWater\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/riemann_solver.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2023 - 2025 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"hyperbolic_system.h\"\n\n#include <simd.h>\n\n#include <deal.II/base/point.h>\n#include <deal.II/base/tensor.h>\n\nnamespace ryujin\n{\n  namespace ShallowWater\n  {\n    template <typename ScalarNumber = double>\n    class RiemannSolverParameters : public dealii::ParameterAcceptor\n    {\n    public:\n      RiemannSolverParameters(const std::string &subsection = \"/RiemannSolver\")\n          : ParameterAcceptor(subsection)\n      {\n      }\n    };\n\n\n    /**\n     * A fast approximative solver for the associated 1D Riemann problem.\n     * The solver has to ensure that the estimate\n     * \\f$\\lambda_{\\text{max}}\\f$ that is returned for the maximal\n     * wavespeed is a strict upper bound.\n     *\n     * @ingroup ShallowWaterEquations\n     */\n    template <int dim, typename Number = double>\n    class RiemannSolver\n    {\n    public:\n      /**\n       * @name Typedefs and constexpr constants\n       */\n      //@{\n\n      using View = HyperbolicSystemView<dim, Number>;\n\n      using ScalarNumber = typename View::ScalarNumber;\n\n      static constexpr auto problem_dimension = View::problem_dimension;\n\n      using state_type = typename View::state_type;\n\n      /**\n       * Number of components in a primitive state, we store \\f$[\\rho, v,\n       * p, a]\\f$, thus, 4.\n       */\n      static constexpr unsigned int riemann_data_size = 3;\n\n      /**\n       * The array type to store the expanded primitive state for the\n       * Riemann solver \\f$[\\rho, v, p, a]\\f$\n       */\n      using primitive_type = typename std::array<Number, riemann_data_size>;\n\n      using precomputed_type = typename View::precomputed_type;\n\n      using PrecomputedVector = typename View::PrecomputedVector;\n\n      using Parameters = RiemannSolverParameters<ScalarNumber>;\n\n      //@}\n      /**\n       * @name Compute wavespeed estimates\n       */\n      //@{\n\n      /**\n       * Constructor taking a HyperbolicSystem instance as argument\n       */\n      RiemannSolver(const HyperbolicSystem &hyperbolic_system,\n                    const Parameters &parameters,\n                    const PrecomputedVector &precomputed_values)\n          : hyperbolic_system(hyperbolic_system)\n          , parameters(parameters)\n          , precomputed_values(precomputed_values)\n      {\n      }\n\n      /**\n       * For two given 1D primitive states riemann_data_i and riemann_data_j,\n       * compute an estimation of an upper bound for the maximum wavespeed\n       * lambda.\n       */\n      Number compute(const primitive_type &riemann_data_i,\n                     const primitive_type &riemann_data_j) const;\n\n      /**\n       * For two given states U_i a U_j and a (normalized) \"direction\" n_ij\n       * compute an estimation of an upper bound for lambda.\n       */\n      Number compute(const state_type &U_i,\n                     const state_type &U_j,\n                     const unsigned int i,\n                     const unsigned int *js,\n                     const dealii::Tensor<1, dim, Number> &n_ij) const;\n\n    protected:\n      //@}\n      /**\n       * @name Internal functions used in the Riemann solver\n       */\n      //@{\n\n      Number f(const primitive_type &primitive_state,\n               const Number &h_star) const;\n\n      Number phi(const primitive_type &riemann_data_i,\n                 const primitive_type &riemann_data_j,\n                 const Number &h) const;\n\n      Number lambda1_minus(const primitive_type &riemann_data,\n                           const Number h_star) const;\n\n      Number lambda3_plus(const primitive_type &riemann_data,\n                          const Number h_star) const;\n\n      Number compute_lambda(const primitive_type &riemann_data_i,\n                            const primitive_type &riemann_data_j,\n                            const Number h_star) const;\n\n    public:\n      Number compute_h_star(const primitive_type &riemann_data_i,\n                            const primitive_type &riemann_data_j) const;\n\n    protected:\n      primitive_type\n      riemann_data_from_state(const state_type &U,\n                              const dealii::Tensor<1, dim, Number> &n_ij) const;\n\n    private:\n      const HyperbolicSystem &hyperbolic_system;\n      const Parameters &parameters;\n      const PrecomputedVector &precomputed_values;\n      //@}\n    };\n  } // namespace ShallowWater\n} // namespace ryujin\n"
  },
  {
    "path": "source/shallow_water/riemann_solver.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// [LANL Copyright Statement]\n// Copyright (C) 2022 - 2024 by the ryujin authors\n// Copyright (C) 2023 - 2024 by Triad National Security, LLC\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"riemann_solver.h\"\n\n#include <newton.h>\n#include <simd.h>\n\n// #define DEBUG_RIEMANN_SOLVER\n\nnamespace ryujin\n{\n  namespace ShallowWater\n  {\n    using namespace dealii;\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::f(const primitive_type &riemann_data_Z,\n                                  const Number &h) const\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n      const ScalarNumber gravity = view.gravity();\n\n      const auto &[h_Z, u_Z, a_Z] = riemann_data_Z;\n\n      const auto left_value = ScalarNumber(2.) * (std::sqrt(gravity * h) - a_Z);\n\n      const Number radicand =\n          ScalarNumber(0.5) * gravity * (h + h_Z) / (h * h_Z);\n      const Number right_value = (h - h_Z) * std::sqrt(radicand);\n\n      return dealii::compare_and_apply_mask<\n          dealii::SIMDComparison::less_than_or_equal>(\n          h, h_Z, left_value, right_value);\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::phi(const primitive_type &riemann_data_i,\n                                    const primitive_type &riemann_data_j,\n                                    const Number &h) const\n    {\n      const Number &u_i = riemann_data_i[1];\n      const Number &u_j = riemann_data_j[1];\n\n#ifdef DEBUG_RIEMANN_SOLVER\n      std::cout << \"f_L --> \" << f(riemann_data_i, h) << std::endl;\n      std::cout << \"f_R --> \" << f(riemann_data_j, h) << std::endl;\n#endif\n      return f(riemann_data_i, h) + f(riemann_data_j, h) + u_j - u_i;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::lambda1_minus(\n        const primitive_type &riemann_data, const Number h_star) const\n    {\n      const auto &[h, u, a] = riemann_data;\n\n      const Number factor = positive_part((h_star - h) / h);\n      const Number half_factor = ScalarNumber(0.5) * factor;\n\n      return u - a * std::sqrt((ScalarNumber(1.) + half_factor) *\n                               (ScalarNumber(1.) + factor));\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::lambda3_plus(const primitive_type &riemann_data,\n                                             const Number h_star) const\n    {\n      const auto &[h, u, a] = riemann_data;\n\n      const Number factor = positive_part((h_star - h) / h);\n      const Number half_factor = ScalarNumber(0.5) * factor;\n\n      return u + a * std::sqrt((ScalarNumber(1.) + half_factor) *\n                               (ScalarNumber(1.) + factor));\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::compute_lambda(\n        const primitive_type &riemann_data_i,\n        const primitive_type &riemann_data_j,\n        const Number h_star) const\n    {\n      const Number lambda1 = lambda1_minus(riemann_data_i, h_star);\n      const Number lambda3 = lambda3_plus(riemann_data_j, h_star);\n\n      return std::max(negative_part(lambda1), positive_part(lambda3));\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline Number\n    RiemannSolver<dim, Number>::compute_h_star(\n        const primitive_type &riemann_data_i,\n        const primitive_type &riemann_data_j) const\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n      const ScalarNumber gravity = view.gravity();\n      const auto gravity_inverse = ScalarNumber(1.) / gravity;\n\n      const auto &[h_i, u_i, a_i] = riemann_data_i;\n      const auto &[h_j, u_j, a_j] = riemann_data_j;\n\n      const Number h_min = std::min(h_i, h_j);\n      const Number h_max = std::max(h_i, h_j);\n\n#ifdef DEBUG_RIEMANN_SOLVER\n      std::cout << h_min << \"  <- h_min/max ->  \" << h_max << std::endl;\n#endif\n\n      const Number a_min = std::sqrt(gravity * h_min);\n      const Number a_max = std::sqrt(gravity * h_max);\n\n#ifdef DEBUG_RIEMANN_SOLVER\n      std::cout << a_min << \"  <- a_min/max ->  \" << a_max << std::endl;\n#endif\n\n      const Number sqrt_two = std::sqrt(ScalarNumber(2.));\n\n      /* x0 = (2 sqrt(2) - 1)^2 */\n      const Number x0 = Number(9.) - ScalarNumber(4.) * sqrt_two;\n\n      const Number phi_value_min =\n          phi(riemann_data_i, riemann_data_j, x0 * h_min);\n#ifdef DEBUG_RIEMANN_SOLVER\n      std::cout << \"phi_value_min ->\" << phi_value_min << std::endl;\n#endif\n\n      const Number phi_value_max =\n          phi(riemann_data_i, riemann_data_j, x0 * h_max);\n#ifdef DEBUG_RIEMANN_SOLVER\n      std::cout << \"phi_value_max ->\" << phi_value_max << std::endl;\n#endif\n\n      /* We compute the three h_star quantities */\n\n      Number tmp;\n\n      /* Double rarefaction case (h_star left): */\n\n      tmp = positive_part(u_i - u_j + ScalarNumber(2.) * (a_i + a_j));\n      const Number h_star_left =\n          ScalarNumber(0.0625) * gravity_inverse * tmp * tmp;\n\n#ifdef DEBUG_RIEMANN_SOLVER\n      std::cout << \"left: \" << h_star_left << std::endl;\n#endif\n\n      /* Double modified shock (h_star middle): */\n\n      tmp = Number(1.) + sqrt_two * (u_i - u_j) / (a_min + a_max);\n      const Number h_star_middle = std::sqrt(h_min * h_max) * tmp;\n\n#ifdef DEBUG_RIEMANN_SOLVER\n      std::cout << \"middle: \" << h_star_middle << std::endl;\n#endif\n\n      /* Expansion and modified shock (h_star right): */\n\n      const auto left_radicand =\n          ScalarNumber(3.) * h_min +\n          ScalarNumber(2.) * sqrt_two * std::sqrt(h_min * h_max);\n\n      const auto right_radicand =\n          sqrt_two * std::sqrt(gravity_inverse * h_min) * (u_i - u_j);\n\n      tmp = std::sqrt(positive_part(left_radicand + right_radicand));\n      tmp -= sqrt_two * std::sqrt(h_min);\n\n      const Number h_star_right = tmp * tmp;\n\n#ifdef DEBUG_RIEMANN_SOLVER\n      std::cout << \"right: \" << h_star_right << std::endl;\n#endif\n\n      /* Finally define h_star */\n\n      Number h_star = dealii::compare_and_apply_mask<\n          dealii::SIMDComparison::less_than_or_equal>(\n          Number(0.), phi_value_min, h_star_left, h_star_right);\n\n      h_star =\n          dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(\n              phi_value_max, Number(0.), h_star_middle, h_star_right);\n\n      return h_star;\n    }\n\n\n    template <int dim, typename Number>\n    DEAL_II_ALWAYS_INLINE inline auto\n    RiemannSolver<dim, Number>::riemann_data_from_state(\n        const state_type &U, const dealii::Tensor<1, dim, Number> &n_ij) const\n        -> primitive_type\n    {\n      const auto view = hyperbolic_system.view<dim, Number>();\n\n      const Number h = view.water_depth_sharp(U);\n      const Number gravity = view.gravity();\n\n      const auto velocity = view.momentum(U) / h;\n      const auto projected_velocity = n_ij * velocity;\n      const auto a = std::sqrt(h * gravity);\n\n      return {{h, projected_velocity, a}};\n    }\n\n\n    template <int dim, typename Number>\n    inline Number RiemannSolver<dim, Number>::compute(\n        const primitive_type &riemann_data_i,\n        const primitive_type &riemann_data_j) const\n    {\n      const Number h_star = compute_h_star(riemann_data_i, riemann_data_j);\n\n      const Number lambda_max =\n          compute_lambda(riemann_data_i, riemann_data_j, h_star);\n\n      return lambda_max;\n    }\n\n\n    template <int dim, typename Number>\n    Number RiemannSolver<dim, Number>::compute(\n        const state_type &U_i,\n        const state_type &U_j,\n        const unsigned int /*i*/,\n        const unsigned int * /*js*/,\n        const dealii::Tensor<1, dim, Number> &n_ij) const\n    {\n      const auto riemann_data_i = riemann_data_from_state(U_i, n_ij);\n      const auto riemann_data_j = riemann_data_from_state(U_j, n_ij);\n      return compute(riemann_data_i, riemann_data_j);\n    }\n\n  } // namespace ShallowWater\n} // namespace ryujin\n"
  },
  {
    "path": "source/simd.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2024 by the ryujin authors\n//\n\n#include \"simd.template.h\"\n\nnamespace ryujin\n{\n#if DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 3\n  template dealii::VectorizedArray<double, 8>\n  pow(const dealii::VectorizedArray<double, 8>, const double);\n\n  template dealii::VectorizedArray<double, 8>\n  pow(const dealii::VectorizedArray<double, 8>,\n      const dealii::VectorizedArray<double, 8>);\n\n  template dealii::VectorizedArray<float, 16>\n  pow(const dealii::VectorizedArray<float, 16>, const float);\n\n  template dealii::VectorizedArray<float, 16>\n  pow(const dealii::VectorizedArray<float, 16>,\n      const dealii::VectorizedArray<float, 16>);\n\n  template dealii::VectorizedArray<double, 8>\n  fast_pow(const dealii::VectorizedArray<double, 8>, const double, const Bias);\n\n  template dealii::VectorizedArray<double, 8>\n  fast_pow(const dealii::VectorizedArray<double, 8>,\n           const dealii::VectorizedArray<double, 8>,\n           const Bias);\n\n  template dealii::VectorizedArray<float, 16>\n  fast_pow(const dealii::VectorizedArray<float, 16>, const float, const Bias);\n\n  template dealii::VectorizedArray<float, 16>\n  fast_pow(const dealii::VectorizedArray<float, 16>,\n           const dealii::VectorizedArray<float, 16>,\n           const Bias);\n#endif\n\n#if DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 2\n  template dealii::VectorizedArray<double, 4>\n  pow(const dealii::VectorizedArray<double, 4>, const double);\n\n  template dealii::VectorizedArray<double, 4>\n  pow(const dealii::VectorizedArray<double, 4>,\n      const dealii::VectorizedArray<double, 4>);\n\n  template dealii::VectorizedArray<float, 8>\n  pow(const dealii::VectorizedArray<float, 8>, const float);\n\n  template dealii::VectorizedArray<float, 8>\n  pow(const dealii::VectorizedArray<float, 8>,\n      const dealii::VectorizedArray<float, 8>);\n\n  template dealii::VectorizedArray<double, 4>\n  fast_pow(const dealii::VectorizedArray<double, 4>, const double, const Bias);\n\n  template dealii::VectorizedArray<double, 4>\n  fast_pow(const dealii::VectorizedArray<double, 4>,\n           const dealii::VectorizedArray<double, 4>,\n           const Bias);\n\n  template dealii::VectorizedArray<float, 8>\n  fast_pow(const dealii::VectorizedArray<float, 8>, const float, const Bias);\n\n  template dealii::VectorizedArray<float, 8>\n  fast_pow(const dealii::VectorizedArray<float, 8>,\n           const dealii::VectorizedArray<float, 8>,\n           const Bias);\n#endif\n\n#if DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 1\n  template dealii::VectorizedArray<double, 2>\n  pow(const dealii::VectorizedArray<double, 2>, const double);\n\n  template dealii::VectorizedArray<double, 2>\n  pow(const dealii::VectorizedArray<double, 2>,\n      const dealii::VectorizedArray<double, 2>);\n\n  template dealii::VectorizedArray<float, 4>\n  pow(const dealii::VectorizedArray<float, 4>, const float);\n\n  template dealii::VectorizedArray<float, 4>\n  pow(const dealii::VectorizedArray<float, 4>,\n      const dealii::VectorizedArray<float, 4>);\n\n  template dealii::VectorizedArray<double, 2>\n  fast_pow(const dealii::VectorizedArray<double, 2>, const double, const Bias);\n\n  template dealii::VectorizedArray<double, 2>\n  fast_pow(const dealii::VectorizedArray<double, 2>,\n           const dealii::VectorizedArray<double, 2>,\n           const Bias);\n\n  template dealii::VectorizedArray<float, 4>\n  fast_pow(const dealii::VectorizedArray<float, 4>, const float, const Bias);\n\n  template dealii::VectorizedArray<float, 4>\n  fast_pow(const dealii::VectorizedArray<float, 4>,\n           const dealii::VectorizedArray<float, 4>,\n           const Bias);\n#endif\n\n  template dealii::VectorizedArray<double, 1>\n  pow(const dealii::VectorizedArray<double, 1>, const double);\n\n  template dealii::VectorizedArray<double, 1>\n  pow(const dealii::VectorizedArray<double, 1>,\n      const dealii::VectorizedArray<double, 1>);\n\n  template dealii::VectorizedArray<float, 1>\n  pow(const dealii::VectorizedArray<float, 1>, const float);\n\n  template dealii::VectorizedArray<float, 1>\n  pow(const dealii::VectorizedArray<float, 1>,\n      const dealii::VectorizedArray<float, 1>);\n\n  template dealii::VectorizedArray<double, 1>\n  fast_pow(const dealii::VectorizedArray<double, 1>, const double, const Bias);\n\n  template dealii::VectorizedArray<double, 1>\n  fast_pow(const dealii::VectorizedArray<double, 1>,\n           const dealii::VectorizedArray<double, 1>,\n           const Bias);\n\n  template dealii::VectorizedArray<float, 1>\n  fast_pow(const dealii::VectorizedArray<float, 1>, const float, const Bias);\n\n  template dealii::VectorizedArray<float, 1>\n  fast_pow(const dealii::VectorizedArray<float, 1>,\n           const dealii::VectorizedArray<float, 1>,\n           const Bias);\n\n} // namespace ryujin\n"
  },
  {
    "path": "source/simd.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <deal.II/base/tensor.h>\n#include <deal.II/base/utilities.h>\n#include <deal.II/base/vectorization.h>\n\n\n/**\n * @name Exception handling in SIMD context\n */\n//@{\n\n\n/**\n * Mixed serial/SIMD variant of the dealii AssertThrow macro. If variable\n * is just a plain double or float, then this macro defaults to a simple\n * call to `dealii::AssertThrow(condition(variable), exception)`. Otherwise\n * (if `decltype(variable)` has a subscript operator `operator[]`, the\n * `dealii::AssertThrow` macro is expanded for all components of the\n * @p variable.\n *\n * @ingroup Miscellaneous\n */\n#define AssertThrowSIMD(variable, condition, exception)                        \\\n  if constexpr (std::is_same<                                                  \\\n                    typename std::remove_const<decltype(variable)>::type,      \\\n                    double>::value ||                                          \\\n                std::is_same<                                                  \\\n                    typename std::remove_const<decltype(variable)>::type,      \\\n                    float>::value) {                                           \\\n    AssertThrow(condition(variable), exception);                               \\\n  } else {                                                                     \\\n    for (unsigned int k = 0; k < decltype(variable)::size(); ++k) {            \\\n      AssertThrow(condition((variable)[k]), exception);                        \\\n    }                                                                          \\\n  }\n\n\nnamespace ryujin\n{\n  //@}\n  /**\n   * @name Type traits and packed index handling\n   */\n  //@{\n\n\n  /**\n   * Small helper class to extract the underlying scalar type of a\n   * VectorizedArray, or return T directly.\n   *\n   * @ingroup SIMD\n   */\n  //@{\n  template <typename T>\n  struct get_value_type {\n    using type = T;\n  };\n\n\n  template <typename T, std::size_t width>\n  struct get_value_type<dealii::VectorizedArray<T, width>> {\n    using type = T;\n  };\n  //@}\n\n\n  /**\n   * Return the stride size:\n   *\n   * @ingroup SIMD\n   */\n  //@{\n  template <typename T>\n  constexpr unsigned int get_stride_size = 1;\n\n  template <typename T, std::size_t width>\n  constexpr unsigned int get_stride_size<dealii::VectorizedArray<T, width>> =\n      width;\n  //@}\n\n\n#ifndef DOXYGEN\n  namespace\n  {\n    template <typename Functor, size_t... Is>\n    auto generate_iterators_impl(Functor f, std::index_sequence<Is...>)\n        -> std::array<decltype(f(0)), sizeof...(Is)>\n    {\n      return {{f(Is)...}};\n    }\n  } /* namespace */\n#endif\n\n\n  /**\n   * Given a callable object f(k), this function creates a std::array with\n   * elements initialized as follows:\n   *\n   *   { f(0) , f(1) , ... , f(length - 1) }\n   *\n   * We use this function to create an array of sparsity iterators that\n   * cannot be default initialized.\n   *\n   * @ingroup SIMD\n   */\n  template <unsigned int length, typename Functor>\n  DEAL_II_ALWAYS_INLINE inline auto generate_iterators(Functor f)\n      -> std::array<decltype(f(0)), length>\n  {\n    return generate_iterators_impl<>(f, std::make_index_sequence<length>());\n  }\n\n\n  /**\n   * Increment all iterators in an std::array simultaneously.\n   *\n   * @ingroup SIMD\n   */\n  template <typename T>\n  DEAL_II_ALWAYS_INLINE inline void increment_iterators(T &iterators)\n  {\n    for (auto &it : iterators)\n      it++;\n  }\n\n  //@}\n  /**\n   * @name Transcendental and other mathematical operations\n   */\n  //@{\n\n  /**\n   * Return the positive part of a number.\n   *\n   * @ingroup SIMD\n   */\n  template <typename Number>\n  inline DEAL_II_ALWAYS_INLINE Number positive_part(const Number number)\n  {\n    return std::max(Number(0.), number);\n  }\n\n\n  /**\n   * Return the negative part of a number.\n   *\n   * @ingroup SIMD\n   */\n  template <typename Number>\n  inline DEAL_II_ALWAYS_INLINE Number negative_part(const Number number)\n  {\n    return -std::min(Number(0.), number);\n  }\n\n\n  /**\n   * A wrapper around dealii::Utilities::fixed_power. We use a wrapper\n   * instead of calling the function directly so that we can easily change\n   * the implementation at one central place.\n   *\n   * @ingroup SIMD\n   */\n  template <int N, typename T>\n  inline T fixed_power(const T x)\n  {\n    return dealii::Utilities::fixed_power<N, T>(x);\n  }\n\n\n  /**\n   * Custom serial pow function.\n   *\n   * @ingroup SIMD\n   */\n  template <typename T>\n  T pow(const T x, const T b);\n\n\n  /**\n   * Custom implementation of a vectorized pow function.\n   *\n   * @ingroup SIMD\n   */\n  template <typename T, std::size_t width>\n  dealii::VectorizedArray<T, width>\n  pow(const dealii::VectorizedArray<T, width> x, const T b);\n\n\n  /**\n   * Custom implementation of a vectorized pow function with vectorized\n   * exponent.\n   *\n   * @ingroup SIMD\n   */\n  template <typename T, std::size_t width>\n  dealii::VectorizedArray<T, width>\n  pow(const dealii::VectorizedArray<T, width> x,\n      const dealii::VectorizedArray<T, width> b);\n\n\n  /**\n   * Controls the bias of the fast_pow() functions.\n   */\n  enum class Bias {\n    /**\n     * No specific bias.\n     */\n    none,\n\n    /**\n     * Guarantee an upper bound, i.e., fast_pow(x,b) >= pow(x,b) provided\n     * that FIXME\n     */\n    max,\n\n    /**\n     * Guarantee a lower bound, i.e., fast_pow(x,b) >= pow(x,b) provided\n     * that FIXME\n     */\n    min\n  };\n\n\n  /**\n   * Custom serial approximate pow function.\n   *\n   * @ingroup SIMD\n   */\n  template <typename T>\n  T fast_pow(const T x, const T b, const Bias bias = Bias::none);\n\n\n  /**\n   * Custom implementation of an approximate, vectorized pow function.\n   *\n   * @ingroup SIMD\n   */\n  template <typename T, std::size_t width>\n  dealii::VectorizedArray<T, width>\n  fast_pow(const dealii::VectorizedArray<T, width> x,\n           const T b,\n           const Bias bias = Bias::none);\n\n\n  /**\n   * Custom implementation of an approximate, vectorized pow function with\n   * vectorized exponent.\n   *\n   * @ingroup SIMD\n   */\n  template <typename T, std::size_t width>\n  dealii::VectorizedArray<T, width>\n  fast_pow(const dealii::VectorizedArray<T, width> x,\n           const dealii::VectorizedArray<T, width> b,\n           const Bias bias = Bias::none);\n\n  //@}\n  /**\n   * @name SIMD based access to vectors and arrays of vectors\n   */\n  //@{\n\n  /**\n   * Return a VectorizedArray with\n   *   { U[i] , U[i + 1] , ... , U[i + VectorizedArray::size() - 1] }\n   *\n   * @ingroup SIMD\n   */\n  template <typename T, typename V>\n  DEAL_II_ALWAYS_INLINE inline T read_entry(const V &vector, unsigned int i)\n  {\n    static_assert(std::is_same_v<typename get_value_type<T>::type,\n                                 typename V::value_type>,\n                  \"type mismatch\");\n    T result;\n\n    if constexpr (std::is_same_v<T, typename get_value_type<T>::type>) {\n      /* Non-vectorized sequential access. */\n      result = vector.local_element(i);\n    } else {\n      /* Vectorized fast access. index must be divisible by simd_length */\n      result.load(vector.get_values() + i);\n    }\n\n    return result;\n  }\n\n\n  /**\n   * Variant of above function specialized for std::vector.\n   * @ingroup SIMD\n   */\n  template <typename T, typename T2>\n  DEAL_II_ALWAYS_INLINE inline T read_entry(const std::vector<T2> &vector,\n                                            unsigned int i)\n  {\n    if constexpr (std::is_same_v<typename get_value_type<T>::type, T2>) {\n      /* Optimized default for source and destination with same type: */\n\n      T result;\n      if constexpr (std::is_same_v<T, typename get_value_type<T>::type>) {\n        /* Non-vectorized sequential access. */\n        result = vector[i];\n      } else {\n        /* Vectorized fast access. index must be divisible by simd_length */\n        result.load(vector.data() + i);\n      }\n      return result;\n\n    } else {\n      /* Fallback for mismatched types (float vs double): */\n      T result;\n      if constexpr (std::is_same_v<T, typename get_value_type<T>::type>) {\n        result = vector[i];\n      } else {\n        // FIXME: suboptimal\n        for (unsigned int k = 0; k < T::size(); ++k)\n          result[k] = vector[i + k];\n      }\n      return result;\n    }\n  }\n\n\n  /**\n   * Return a VectorizedArray with\n   *   { U[js[0] , U[js[1]] , ... , U[js[VectorizedArray::size() - 1]] }\n   *\n   * @ingroup SIMD\n   */\n  template <typename T, typename V>\n  DEAL_II_ALWAYS_INLINE inline T read_entry(const V &vector,\n                                            const unsigned int *js)\n  {\n    static_assert(std::is_same_v<typename get_value_type<T>::type,\n                                 typename V::value_type>,\n                  \"type mismatch\");\n    T result;\n\n    if constexpr (std::is_same_v<T, typename get_value_type<T>::type>) {\n      /* Non-vectorized sequential access. */\n      result = vector.local_element(js[0]);\n    } else {\n      /* Vectorized fast access. index must be divisible by simd_length */\n      result.gather(vector.get_values(), js);\n    }\n\n    return result;\n  }\n\n\n  /**\n   * Variant of above function specialized for std::vector.\n   * @ingroup SIMD\n   */\n  template <typename T, typename T2>\n  DEAL_II_ALWAYS_INLINE inline T read_entry(const std::vector<T2> &vector,\n                                            const unsigned int *js)\n  {\n    static_assert(std::is_same_v<typename get_value_type<T>::type, T2>,\n                  \"type mismatch\");\n    T result;\n\n    if constexpr (std::is_same_v<T, typename get_value_type<T>::type>) {\n      /* Non-vectorized sequential access. */\n      result = vector[js[0]];\n    } else {\n      /* Vectorized fast access. index must be divisible by simd_length */\n      result.load(vector.data(), js);\n    }\n\n    return result;\n  }\n\n\n  /**\n   * Write out the given VectorizedArray to the vector\n   *\n   * @ingroup SIMD\n   */\n  template <typename T, typename V>\n  DEAL_II_ALWAYS_INLINE inline void\n  write_entry(V &vector, const T &values, unsigned int i)\n  {\n    static_assert(std::is_same_v<typename get_value_type<T>::type,\n                                 typename V::value_type>,\n                  \"type mismatch\");\n\n    if constexpr (std::is_same_v<T, typename get_value_type<T>::type>) {\n      /* Non-vectorized sequential access. */\n      vector.local_element(i) = values;\n    } else {\n      /* Vectorized fast access. index must be divisible by simd_length */\n      values.store(vector.get_values() + i);\n    }\n  }\n\n\n  /**\n   * Variant of above function specialized for std::vector.\n   * @ingroup SIMD\n   */\n  template <typename T, typename T2>\n  DEAL_II_ALWAYS_INLINE inline void\n  write_entry(std::vector<T2> &vector, const T &values, unsigned int i)\n  {\n    if constexpr (std::is_same_v<typename get_value_type<T>::type, T2>) {\n      /* Optimized default for source and destination with same type: */\n\n      if constexpr (std::is_same_v<T, typename get_value_type<T>::type>) {\n        /* Non-vectorized sequential access. */\n        vector[i] = values;\n      } else {\n        /* Vectorized fast access. index must be divisible by simd_length */\n        values.store(vector.data() + i);\n      }\n\n    } else {\n      /* Fallback for mismatched types (float vs double): */\n      if constexpr (std::is_same_v<T, typename get_value_type<T>::type>) {\n        vector[i] = values;\n      } else {\n        // FIXME: suboptimal\n        for (unsigned int k = 0; k < T::size(); ++k)\n          vector[i + k] = values[k];\n      }\n    }\n  }\n\n\n  /**\n   * Return the k-th serialized component of a Tensor of VectorizedArray\n   *\n   * @ingroup SIMD\n   */\n  template <int rank, int dim, std::size_t width, typename Number>\n  DEAL_II_ALWAYS_INLINE inline dealii::Tensor<rank, dim, Number>\n  serialize_tensor(\n      const dealii::Tensor<rank, dim, dealii::VectorizedArray<Number, width>>\n          &vectorized,\n      const unsigned int k)\n  {\n    Assert(k < width, dealii::ExcMessage(\"Index past VectorizedArray width\"));\n    dealii::Tensor<rank, dim, Number> result;\n    if constexpr (rank == 1) {\n      for (unsigned int d = 0; d < dim; ++d)\n        result[d] = vectorized[d][k];\n    } else {\n      for (unsigned int d = 0; d < dim; ++d)\n        result[d] = serialize_tensor(vectorized[d], k);\n    }\n    return result;\n  }\n\n\n  /**\n   * Variant of above function for serial Tensors that simply returns the\n   * given tensor.\n   *\n   * @ingroup SIMD\n   */\n  template <int rank, int dim, typename Number>\n  DEAL_II_ALWAYS_INLINE inline dealii::Tensor<rank, dim, Number>\n  serialize_tensor(const dealii::Tensor<rank, dim, Number> &serial,\n                   const unsigned int k [[maybe_unused]])\n  {\n    Assert(k == 0,\n           dealii::ExcMessage(\n               \"The given index k must be zero for a serial tensor\"));\n    return serial;\n  }\n\n\n  /**\n   * Update the the k-th serial component of a Tensor of VectorizedArray\n   *\n   * @ingroup SIMD\n   */\n  template <int rank, int dim, std::size_t width, typename Number>\n  DEAL_II_ALWAYS_INLINE inline void assign_serial_tensor(\n      dealii::Tensor<rank, dim, dealii::VectorizedArray<Number, width>> &result,\n      const dealii::Tensor<rank, dim, Number> &serial,\n      const unsigned int k)\n  {\n    Assert(k < width, dealii::ExcMessage(\"Index past VectorizedArray width\"));\n    if constexpr (rank == 1) {\n      for (unsigned int d = 0; d < dim; ++d)\n        result[d][k] = serial[d];\n    } else {\n      for (unsigned int d = 0; d < dim; ++d)\n        assign_serial_tensor(result[d], serial[d], k);\n    }\n  }\n\n\n  /**\n   * Variant of above function for serial Tensors that simply assigns the\n   * given tensor as is.\n   *\n   * @ingroup SIMD\n   */\n  template <int rank, int dim, typename Number>\n  DEAL_II_ALWAYS_INLINE inline void\n  assign_serial_tensor(dealii::Tensor<rank, dim, Number> &result,\n                       const dealii::Tensor<rank, dim, Number> &serial,\n                       const unsigned int k [[maybe_unused]])\n  {\n    Assert(k == 0,\n           dealii::ExcMessage(\n               \"The given index k must be zero for a serial tensor\"));\n\n    result = serial;\n  }\n\n  //@}\n\n} // namespace ryujin\n"
  },
  {
    "path": "source/simd.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2024 by the ryujin authors\n//\n\n#pragma once\n\n#include \"simd.h\"\n#include \"simd_fast_pow.template.h\"\n\n#include <cmath>\n\n#if DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 1 && defined(__SSE2__)\n// Import the vectorlib library prefixed in the vcl namespace\n#define VCL_NAMESPACE vcl\nDEAL_II_DISABLE_EXTRA_DIAGNOSTICS\n#include \"../simd-math/vectorclass.h\"\n#include \"../simd-math/vectormath_exp.h\"\nDEAL_II_ENABLE_EXTRA_DIAGNOSTICS\n#undef VCL_NAMESPACE\n#else\n// Make ryujin::pow known as vcl::ryujin\nnamespace vcl = ryujin;\n#endif\n\nnamespace ryujin\n{\n  /*****************************************************************************\n   *                Helper typetraits for dealing with vcl:                    *\n   ****************************************************************************/\n\n  namespace\n  {\n    /*\n     * A type trait to select the correct VCL type:\n     */\n    template <typename T, std::size_t width>\n    struct VectorClassType {\n    };\n\n#if DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 3 && defined(__AVX512F__)\n    template <>\n    struct VectorClassType<float, 16> {\n      using value_type = vcl::Vec16f;\n    };\n\n    template <>\n    struct VectorClassType<double, 8> {\n      using value_type = vcl::Vec8d;\n    };\n#endif\n\n#if DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 2 && defined(__AVX__)\n    template <>\n    struct VectorClassType<float, 8> {\n      using value_type = vcl::Vec8f;\n    };\n\n    template <>\n    struct VectorClassType<double, 4> {\n      using value_type = vcl::Vec4d;\n    };\n#endif\n\n#if DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 1 && defined(__SSE2__)\n    template <>\n    struct VectorClassType<float, 4> {\n      using value_type = vcl::Vec4f;\n    };\n\n    template <>\n    struct VectorClassType<double, 2> {\n      using value_type = vcl::Vec2d;\n    };\n\n    template <>\n    struct VectorClassType<float, 1> {\n      using value_type = vcl::Vec4f;\n    };\n\n    template <>\n    struct VectorClassType<double, 1> {\n      using value_type = vcl::Vec2d;\n    };\n\n#else\n    template <>\n    struct VectorClassType<float, 1> {\n      using value_type = float;\n    };\n\n    template <>\n    struct VectorClassType<double, 1> {\n      using value_type = double;\n    };\n#endif\n\n    /*\n     * Convert a dealii::VectorizedArray to a VCL container type:\n     */\n    template <typename T, std::size_t width>\n    DEAL_II_ALWAYS_INLINE inline typename VectorClassType<T, width>::value_type\n    to_vcl(const dealii::VectorizedArray<T, width> x)\n    {\n      return typename VectorClassType<T, width>::value_type(x.data);\n    }\n\n\n    /*\n     * Convert a VCL container type to a dealii::VectorizedArray:\n     */\n    template <typename T, std::size_t width>\n    DEAL_II_ALWAYS_INLINE inline dealii::VectorizedArray<T, width>\n    from_vcl(typename VectorClassType<T, width>::value_type x)\n    {\n      dealii::VectorizedArray<T, width> result;\n#if DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 1 && defined(__SSE2__)\n      if constexpr (width == 1)\n        result.data = x.extract(0);\n      else\n#endif\n        result.data = x;\n      return result;\n    }\n\n\n    /*\n     * Helper functions to convert to float arrays and back.\n     */\n    template <typename T, std::size_t width>\n    struct FC {\n    };\n\n    template <std::size_t width>\n    struct FC<double, width> {\n      // There is no Vec2f, so make sure to use Vec4f instead\n#if DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 1 && defined(__SSE2__)\n      static constexpr std::size_t float_width = (width <= 2 ? 4 : width);\n#else\n      static_assert(width == 1, \"internal error\");\n      static constexpr std::size_t float_width = width;\n#endif\n\n      static DEAL_II_ALWAYS_INLINE inline\n          typename VectorClassType<float, float_width>::value_type\n          to_float(typename VectorClassType<double, width>::value_type x)\n      {\n#if DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 1 && defined(__SSE2__)\n        return vcl::to_float(x);\n#else\n        return x;\n#endif\n      }\n\n      static DEAL_II_ALWAYS_INLINE inline\n          typename VectorClassType<double, width>::value_type\n          to_double(typename VectorClassType<float, float_width>::value_type x)\n      {\n#if DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 1 && defined(__SSE2__)\n        if constexpr (width == 1) {\n          return static_cast<double>(x.extract(0));\n        } else if constexpr (width == 2) {\n          const vcl::Vec4d temp = vcl::to_double(x);\n          return vcl::Vec2d(temp.extract(0), temp.extract(1));\n        } else {\n          return vcl::to_double(x);\n        }\n#else\n        return x;\n#endif\n      }\n    };\n\n    template <std::size_t width>\n    struct FC<float, width> {\n      static DEAL_II_ALWAYS_INLINE inline\n          typename VectorClassType<float, width>::value_type\n          to_float(typename VectorClassType<float, width>::value_type x)\n      {\n        return x;\n      }\n      static DEAL_II_ALWAYS_INLINE inline\n          typename VectorClassType<float, width>::value_type\n          to_double(typename VectorClassType<float, width>::value_type x)\n      {\n        return x;\n      }\n    };\n  } // namespace\n\n\n  /*****************************************************************************\n   *                           pow() implementation:                           *\n   ****************************************************************************/\n\n#if DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 1 && defined(__SSE2__)\n  template <>\n  // DEAL_II_ALWAYS_INLINE inline\n  float pow(const float x, const float b)\n  {\n    /* Use a custom pow implementation instead of std::pow(): */\n    return vcl::pow(vcl::Vec4f(x), b).extract(0);\n  }\n\n\n  template <>\n  // DEAL_II_ALWAYS_INLINE inline\n  double pow(const double x, const double b)\n  {\n    /* Use a custom pow implementation instead of std::pow(): */\n    return vcl::pow(vcl::Vec2d(x), b).extract(0);\n  }\n\n\n  template <typename T, std::size_t width>\n  // DEAL_II_ALWAYS_INLINE inline\n  dealii::VectorizedArray<T, width>\n  pow(const dealii::VectorizedArray<T, width> x, const T b)\n  {\n    return from_vcl<T, width>(vcl::pow(to_vcl(x), b));\n  }\n\n\n  template <typename T, std::size_t width>\n  // DEAL_II_ALWAYS_INLINE inline\n  dealii::VectorizedArray<T, width>\n  pow(const dealii::VectorizedArray<T, width> x,\n      const dealii::VectorizedArray<T, width> b)\n  {\n    return from_vcl<T, width>(vcl::pow(to_vcl(x), to_vcl(b)));\n  }\n\n#else\n\n  template <>\n  // DEAL_II_ALWAYS_INLINE inline\n  float pow(const float x, const float b)\n  {\n    // Call generic std::pow() implementation\n    return std::pow(x, b);\n  }\n\n\n  template <>\n  // DEAL_II_ALWAYS_INLINE inline\n  double pow(const double x, const double b)\n  {\n    // Call generic std::pow() implementation\n    return std::pow(x, b);\n  }\n\n\n  template <typename T, std::size_t width>\n  // DEAL_II_ALWAYS_INLINE inline\n  dealii::VectorizedArray<T, width>\n  pow(const dealii::VectorizedArray<T, width> x, const T b)\n  {\n    // Call generic deal.II implementation\n    return std::pow(x, b);\n  }\n\n\n  template <typename T, std::size_t width>\n  // DEAL_II_ALWAYS_INLINE inline\n  dealii::VectorizedArray<T, width>\n  pow(const dealii::VectorizedArray<T, width> x,\n      const dealii::VectorizedArray<T, width> b)\n  {\n    // Call generic deal.II implementation\n    return std::pow(x, b);\n  }\n#endif\n\n\n  /*****************************************************************************\n   *                         Fast pow() implementation:                        *\n   ****************************************************************************/\n\n#if DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 1 && defined(__SSE2__)\n  template <>\n  // DEAL_II_ALWAYS_INLINE inline\n  float fast_pow(const float x, const float b, const Bias bias)\n  {\n    /* Use a custom pow implementation instead of std::pow(): */\n    return fast_pow_impl(vcl::Vec4f(x), vcl::Vec4f(b), bias).extract(0);\n  }\n\n\n  template <>\n  // DEAL_II_ALWAYS_INLINE inline\n  double fast_pow(const double x, const double b, const Bias bias)\n  {\n    /* Use a custom pow implementation instead of std::pow(): */\n    return fast_pow_impl(vcl::Vec4f(x), vcl::Vec4f(b), bias).extract(0);\n  }\n\n\n  template <typename T, std::size_t width>\n  // DEAL_II_ALWAYS_INLINE inline\n  dealii::VectorizedArray<T, width> fast_pow(\n      const dealii::VectorizedArray<T, width> x, const T b, const Bias bias)\n  {\n    using vcl_type = decltype(FC<T, width>::to_float(to_vcl(x)));\n    return from_vcl<T, width>(FC<T, width>::to_double(\n        fast_pow_impl(FC<T, width>::to_float(to_vcl(x)), vcl_type(b), bias)));\n  }\n\n\n  template <typename T, std::size_t width>\n  // DEAL_II_ALWAYS_INLINE inline\n  dealii::VectorizedArray<T, width>\n  fast_pow(const dealii::VectorizedArray<T, width> x,\n           const dealii::VectorizedArray<T, width> b,\n           const Bias bias)\n  {\n    return from_vcl<T, width>(\n        FC<T, width>::to_double(fast_pow_impl(FC<T, width>::to_float(to_vcl(x)),\n                                              FC<T, width>::to_float(to_vcl(b)),\n                                              bias)));\n  }\n\n#else\n\n  template <>\n  // DEAL_II_ALWAYS_INLINE inline\n  float fast_pow(const float x, const float b, const Bias)\n  {\n    // Call generic std::pow() implementation\n    return std::pow(x, b);\n  }\n\n\n  template <>\n  // DEAL_II_ALWAYS_INLINE inline\n  double fast_pow(const double x, const double b, const Bias)\n  {\n    // Call generic std::pow() implementation\n    return std::pow(static_cast<float>(x), static_cast<float>(b));\n  }\n\n\n  template <typename T, std::size_t width>\n  // DEAL_II_ALWAYS_INLINE inline\n  dealii::VectorizedArray<T, width>\n  fast_pow(const dealii::VectorizedArray<T, width> x, const T b, const Bias)\n  {\n    // Call generic deal.II implementation\n    return std::pow(x, b);\n  }\n\n\n  template <typename T, std::size_t width>\n  // DEAL_II_ALWAYS_INLINE inline\n  dealii::VectorizedArray<T, width>\n  fast_pow(const dealii::VectorizedArray<T, width> x,\n           const dealii::VectorizedArray<T, width> b,\n           const Bias)\n  {\n    // Call generic deal.II implementation\n    return std::pow(x, b);\n  }\n#endif\n\n\n} // namespace ryujin\n"
  },
  {
    "path": "source/simd_fast_pow.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0\n// Copyright (C) 2014 - 2022 by Agner Fog\n// Copyright (C) 2023 by the ryujin authors\n//\n\n#pragma once\n\n#include \"simd.h\"\n\n#include <cmath>\n\n#if DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 1 && defined(__SSE2__)\n#define VCL_NAMESPACE vcl\nDEAL_II_DISABLE_EXTRA_DIAGNOSTICS\n#include \"../simd-math/vectorclass.h\"\n#include \"../simd-math/vectormath_exp.h\"\nDEAL_II_ENABLE_EXTRA_DIAGNOSTICS\n#undef VCL_NAMESPACE\n\n#else\nnamespace ryujin\n{\n  template <typename T>\n  T fast_pow_impl(const T x, const T b, const Bias)\n  {\n    return std::pow(x, b);\n  }\n} // namespace ryujin\n#endif\n\n\n#if DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 1 && defined(__SSE2__)\nnamespace ryujin\n{\n  template <typename VTYPE>\n  inline DEAL_II_ALWAYS_INLINE VTYPE fast_pow_impl(VTYPE const x0,\n                                                   VTYPE const y,\n                                                   Bias)\n  {\n    /* clang-format off */\n    using namespace vcl;\n\n    const float ln2f_hi  =  0.693359375f;        // log(2), split in two for extended precision\n    const auto log2e = static_cast<float>(VM_LOG2E); // 1/log(2)\n\n    const float P0logf  =  3.3333331174E-1f;     // coefficients for logarithm expansion\n    const float P1logf  = -2.4999993993E-1f;\n    const float P2logf  =  2.0000714765E-1f;\n    const float P3logf  = -1.6668057665E-1f;\n    const float P4logf  =  1.4249322787E-1f;\n    const float P5logf  = -1.2420140846E-1f;\n    const float P6logf  =  1.1676998740E-1f;\n    const float P7logf  = -1.1514610310E-1f;\n    const float P8logf  =  7.0376836292E-2f;\n\n    const float p2expf   =  1.f/2.f;             // coefficients for Taylor expansion of exp\n    const float p3expf   =  1.f/6.f;\n    const float p4expf   =  1.f/24.f;\n    const float p5expf   =  1.f/120.f;\n    const float p6expf   =  1.f/720.f;\n    const float p7expf   =  1.f/5040.f;\n\n    typedef decltype(roundi(x0)) ITYPE;          // integer vector type\n    typedef decltype(x0 < x0) BVTYPE;            // boolean vector type\n\n    // data vectors\n    VTYPE x, x1, x2;                             // x variable\n    VTYPE ef, e1, e2, e3, ee;                    // exponent\n    VTYPE yr;                                    // remainder\n    VTYPE lg, lg1, lgerr, x2err, v;              // logarithm\n    VTYPE z;                                     // pow(x,y)\n    VTYPE yodd(0);                               // has sign bit set if y is an odd integer\n    // integer vectors\n    ITYPE ei, ej;                                // exponent\n    // boolean vectors\n    BVTYPE blend, xzero;                  // x conditions\n    BVTYPE overflow, underflow;           // error conditions\n\n    // remove sign\n    x1 = abs(x0);\n\n    // Separate mantissa from exponent\n    // This gives the mantissa * 0.5\n    x  = fraction_2(x1);\n\n    // reduce range of x = +/- sqrt(2)/2\n    blend = x > static_cast<float>(VM_SQRT2 * 0.5);\n    x  = if_add(!blend, x, x);                   // conditional add\n\n    // Taylor expansion, high precision\n    x   -= 1.0f;\n    x2   = x * x;\n    lg1  = polynomial_8(x, P0logf, P1logf, P2logf, P3logf, P4logf, P5logf, P6logf, P7logf, P8logf);\n    lg1 *= x2 * x;\n\n    // extract exponent\n    ef = exponent_f(x1);\n    ef = if_add(blend, ef, 1.0f);                // conditional add\n\n    // multiply exponent by y\n    // nearest integer e1 goes into exponent of result, remainder yr is added to log\n    e1 = round(ef * y);\n    yr = mul_sub(ef, y, e1);                   // calculate remainder yr. precision very important here\n\n    // add initial terms to expansion\n    lg = nmul_add(0.5f, x2, x) + lg1;            // lg = (x - 0.5f * x2) + lg1;\n\n    // calculate rounding errors in lg\n    // rounding error in multiplication 0.5*x*x\n    x2err = mul_sub(0.5f*x, x, 0.5f * x2);\n    // rounding error in additions and subtractions\n    lgerr = mul_add(0.5f, x2, lg - x) - lg1;     // lgerr = ((lg - x) + 0.5f * x2) - lg1;\n\n    // extract something for the exponent\n    e2 = round(lg * y * static_cast<float>(VM_LOG2E));\n    // subtract this from lg, with extra precision\n    v = mul_sub(lg, y, e2 * ln2f_hi);\n\n    // correct for previous rounding errors\n    v -= mul_sub(lgerr + x2err, y, yr * static_cast<float>(VM_LN2)); // v -= (lgerr + x2err) * y - yr * float(VM_LN2) ;\n\n    // exp function\n\n    // extract something for the exponent if possible\n    x = v;\n    e3 = round(x*log2e);\n    // high precision multiplication not needed here because abs(e3) <= 1\n    x = nmul_add(e3, static_cast<float>(VM_LN2), x);          // x -= e3 * float(VM_LN2);\n\n    // Taylor polynomial\n    x2  = x  * x;\n    z = polynomial_5(x, p2expf, p3expf, p4expf, p5expf, p6expf, p7expf)*x2 + x + 1.0f;\n\n    // contributions to exponent\n    ee = e1 + e2 + e3;\n    ei = roundi(ee);\n    // biased exponent of result:\n    ej = ei + (ITYPE(reinterpret_i(abs(z))) >> 23);\n\n    // add exponent by integer addition\n    z = reinterpret_f(ITYPE(reinterpret_i(z)) + (ei << 23)); // the extra 0x10000 is shifted out here\n\n\n    // check for overflow and underflow\n\n    overflow = BVTYPE(ej >= 0x0FF) | (ee > 300.f);\n    underflow = BVTYPE(ej <= 0x000) | (ee < -300.f);\n    if (horizontal_or(overflow | underflow)) {\n      // handle errors\n      z = select(underflow, VTYPE(0.f), z);\n      z = select(overflow, infinite_vec<VTYPE>(), z);\n    }\n\n    // check for x == 0\n\n    xzero = is_zero_or_subnormal(x0);\n    z = wm_pow_case_x0(xzero, y, z);\n\n    return z;\n\n    /* clang-format on */\n  }\n} // namespace ryujin\n\n#endif\n"
  },
  {
    "path": "source/skeleton/CMakeLists.txt",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2023 - 2024 by the ryujin authors\n##\n\nadd_library(obj_skeleton OBJECT\n  equation_dispatch.cc\n  initial_state_library.cc\n  )\nset_target_properties(obj_skeleton PROPERTIES LINKER_LANGUAGE CXX)\ndeal_ii_setup_target(obj_skeleton)\ntarget_link_libraries(obj_skeleton obj_common ${EXTERNAL_TARGETS})\n# Propagate the current source directory with PUBLIC visibility\ntarget_include_directories(obj_skeleton PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})\n"
  },
  {
    "path": "source/skeleton/Makefile",
    "content": "default: all\n.PHONY: default\n\n%:\n\t@cd .. && make $@\n.PHONY: %\n"
  },
  {
    "path": "source/skeleton/description.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"../stub_parabolic_module.h\"\n#include \"../stub_parabolic_system.h\"\n#include \"hyperbolic_system.h\"\n#include \"indicator.h\"\n#include \"limiter.h\"\n#include \"riemann_solver.h\"\n\nnamespace ryujin\n{\n  namespace Skeleton\n  {\n    /**\n     * A struct that contains all equation specific classes describing the\n     * chosen hyperbolic system, the indicator, the limiter and\n     * (approximate) Riemann solver.\n     *\n     * We group all of these templates together in this struct so that we\n     * only need to add a single template parameter to the all the\n     * algorithm classes, such as HyperbolicModule.\n     *\n     * @ingroup SkeletonEquations\n     */\n    struct Description {\n      using HyperbolicSystem = Skeleton::HyperbolicSystem;\n\n      template <int dim, typename Number = double>\n      using HyperbolicSystemView = Skeleton::HyperbolicSystemView<dim, Number>;\n\n      using ParabolicSystem = ryujin::StubParabolicSystem;\n\n      template <int dim, typename Number = double>\n      using ParabolicModule =\n          ryujin::StubParabolicModule<Description, dim, Number>;\n\n      template <int dim, typename Number = double>\n      using Indicator = Skeleton::Indicator<dim, Number>;\n\n      template <int dim, typename Number = double>\n      using Limiter = Skeleton::Limiter<dim, Number>;\n\n      template <int dim, typename Number = double>\n      using RiemannSolver = Skeleton::RiemannSolver<dim, Number>;\n    };\n  } // namespace Skeleton\n} // namespace ryujin\n"
  },
  {
    "path": "source/skeleton/equation_dispatch.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2024 by the ryujin authors\n//\n\n#include \"description.h\"\n\n#include <compile_time_options.h>\n#include <equation_dispatch.h>\n\nnamespace ryujin\n{\n  namespace Skeleton\n  {\n    Dispatch<Description, NUMBER> dispatch_instance(\"skeleton\");\n  } // namespace Skeleton\n} // namespace ryujin\n"
  },
  {
    "path": "source/skeleton/hyperbolic_system.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <convenience_macros.h>\n#include <discretization.h>\n#include <loop.h>\n#include <multicomponent_vector.h>\n#include <patterns_conversion.h>\n#include <simd.h>\n#include <state_vector.h>\n\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/tensor.h>\n\n#include <array>\n\nnamespace ryujin\n{\n  namespace Skeleton\n  {\n    template <int dim, typename Number>\n    class HyperbolicSystemView;\n\n    /**\n     * Description of a hyperbolic conservation law.\n     *\n     * @ingroup SkeletonEquations\n     */\n    class HyperbolicSystem final : public dealii::ParameterAcceptor\n    {\n    public:\n      /**\n       * The name of the hyperbolic system as a string.\n       */\n      static inline const std::string problem_name =\n          \"Skeleton Hyperbolic System\";\n\n      /**\n       * Constructor.\n       */\n      HyperbolicSystem(const std::string &subsection = \"/HyperbolicSystem\")\n          : ParameterAcceptor(subsection)\n      {\n      }\n\n      /**\n       * Return a view on the Hyperbolic System for a given dimension @p\n       * dim and choice of number type @p Number (which can be a scalar\n       * float, or double, as well as a VectorizedArray holding packed\n       * scalars.\n       */\n      template <int dim, typename Number>\n      auto view() const\n      {\n        return HyperbolicSystemView<dim, Number>{*this};\n      }\n\n      /**\n       * Part of step 1 of the hyperbolic update step: Compute \"precomputed\n       * values\" and fill into the state vector.\n       *\n       * @note The method does not update the ghost range of the state\n       * vector. The precomputed part has to be synchronized by explicitly\n       * calling the update ghost values function.\n       */\n      template <int dim, typename ScalarNumber>\n      void fill_precomputed_values(\n          const OfflineData<dim, ScalarNumber> & /*offline_data*/,\n          typename HyperbolicSystemView<dim, ScalarNumber>::StateVector\n              & /*state_vector*/,\n          const bool /*skip_constrained_dofs*/ = true) const\n      {\n        // nothing to do\n      }\n\n    private:\n      template <int dim, typename Number>\n      friend class HyperbolicSystemView;\n    }; /* HyperbolicSystem */\n\n\n    /**\n     * A view on the HyperbolicSystem for a given dimension @p dim and\n     * choice of number type @p Number (which can be a scalar float, or\n     * double, as well as a VectorizedArray holding packed scalars.\n     */\n    template <int dim, typename Number>\n    class HyperbolicSystemView\n    {\n    public:\n      /**\n       * Constructor taking a reference to the underlying\n       * HyperbolicSystem\n       */\n      HyperbolicSystemView(const HyperbolicSystem &hyperbolic_system)\n          : hyperbolic_system_(hyperbolic_system)\n      {\n      }\n\n      /**\n       * Create a modified view from the current one:\n       */\n      template <int dim2, typename Number2>\n      auto view() const\n      {\n        return HyperbolicSystemView<dim2, Number2>{hyperbolic_system_};\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system_;\n\n\n    public:\n      /**\n       * @name Types and constexpr constants\n       */\n      //@{\n\n      /**\n       * The underlying scalar number type.\n       */\n      using ScalarNumber = typename get_value_type<Number>::type;\n\n      /**\n       * The dimension of the state space.\n       */\n      static constexpr unsigned int problem_dimension = 1;\n\n      /**\n       * Storage type for a (conserved) state vector \\f$\\boldsymbol U\\f$.\n       */\n      using state_type = dealii::Tensor<1, problem_dimension, Number>;\n\n      /**\n       * Storage type for the flux \\f$\\mathbf{f}\\f$.\n       */\n      using flux_type =\n          dealii::Tensor<1, problem_dimension, dealii::Tensor<1, dim, Number>>;\n\n      /**\n       * The storage type used for flux contributions.\n       */\n      using flux_contribution_type = flux_type;\n\n      /**\n       * An array holding all component names of the conserved state as a\n       * string.\n       */\n      static inline const auto component_names =\n          []() -> std::array<std::string, problem_dimension> {\n        if constexpr (dim == 1)\n          return {\"u\"};\n        else if constexpr (dim == 2)\n          return {\"u\"};\n        else if constexpr (dim == 3)\n          return {\"u\"};\n        __builtin_trap();\n      }();\n\n      /**\n       * An array holding all component names of the primitive state as a\n       * string.\n       */\n      static inline const auto primitive_component_names =\n          []() -> std::array<std::string, problem_dimension> {\n        if constexpr (dim == 1)\n          return {\"u\"};\n        else if constexpr (dim == 2)\n          return {\"u\"};\n        else if constexpr (dim == 3)\n          return {\"u\"};\n        __builtin_trap();\n      }();\n\n      /**\n       * The number of precomputed values.\n       */\n      static constexpr unsigned int n_precomputed_values = 0;\n\n      /**\n       * Array type used for precomputed values.\n       */\n      using precomputed_type = std::array<Number, n_precomputed_values>;\n\n      /**\n       * An array holding all component names of the precomputed values.\n       */\n      static inline const auto precomputed_names =\n          std::array<std::string, n_precomputed_values>{};\n\n      /**\n       * The number of precomputed initial values.\n       */\n      static constexpr unsigned int n_initial_precomputed_values = 0;\n\n      /**\n       * Array type used for precomputed initial values.\n       */\n      using initial_precomputed_type =\n          std::array<Number, n_initial_precomputed_values>;\n\n      /**\n       * An array holding all component names of the precomputed values.\n       */\n      static inline const auto initial_precomputed_names =\n          std::array<std::string, n_initial_precomputed_values>{};\n\n      /**\n       * A compound state vector.\n       */\n      using StateVector = Vectors::\n          StateVector<ScalarNumber, problem_dimension, n_precomputed_values>;\n\n      /**\n       * MulticomponentVector for storing the hyperbolic state vector:\n       */\n      using HyperbolicVector =\n          Vectors::MultiComponentVector<ScalarNumber, problem_dimension>;\n\n      /**\n       * MulticomponentVector for storing a vector of precomputed states:\n       */\n      using PrecomputedVector =\n          Vectors::MultiComponentVector<ScalarNumber, n_precomputed_values>;\n\n      /**\n       * MulticomponentVector for storing a vector of precomputed initial\n       * states:\n       */\n      using InitialPrecomputedVector =\n          Vectors::MultiComponentVector<ScalarNumber,\n                                        n_initial_precomputed_values>;\n\n      //@}\n      /**\n       * @name Computing derived physical quantities\n       */\n      //@{\n\n      /**\n       * Returns whether the state @p U is admissible. If @p U is a\n       * vectorized state then @p U is admissible if all vectorized\n       * values are admissible.\n       */\n      bool is_admissible(const state_type & /*U*/) const\n      {\n        return true;\n      }\n\n      //@}\n      /**\n       * @name Special functions for boundary states\n       */\n      //@{\n\n      /**\n       * Apply boundary conditions.\n       */\n      template <typename Lambda>\n      state_type apply_boundary_conditions(\n          const dealii::types::boundary_id /*id*/,\n          const state_type &U,\n          const dealii::Tensor<1, dim, Number> & /*normal*/,\n          const Lambda & /*get_dirichlet_data*/) const\n      {\n        return U;\n      }\n\n      //@}\n      /**\n       * @name Flux computations\n       */\n      //@{\n\n      /**\n       * Given a state @p U_i and an index @p i compute flux contributions.\n       *\n       * Intended usage:\n       * ```\n       * Indicator<dim, Number> indicator;\n       * for (unsigned int i = n_internal; i < n_owned; ++i) {\n       *   // ...\n       *   const auto flux_i = flux_contribution(precomputed..., i, U_i);\n       *   for (unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {\n       *     // ...\n       *     const auto flux_j = flux_contribution(precomputed..., js, U_j);\n       *     const auto flux_ij = flux_divergence(flux_i, flux_j, c_ij);\n       *   }\n       * }\n       * ```\n       *\n       * For the Euler equations we simply compute <code>f(U_i)</code>.\n       */\n      flux_contribution_type\n      flux_contribution(const PrecomputedVector & /*pv*/,\n                        const InitialPrecomputedVector & /*piv*/,\n                        const unsigned int /*i*/,\n                        const state_type & /*U_i*/) const\n      {\n        return flux_contribution_type{};\n      }\n\n      flux_contribution_type\n      flux_contribution(const PrecomputedVector & /*pv*/,\n                        const InitialPrecomputedVector & /*piv*/,\n                        const unsigned int * /*js*/,\n                        const state_type & /*U_j*/) const\n      {\n        return flux_contribution_type{};\n      }\n\n      /**\n       * Given flux contributions @p flux_i and @p flux_j compute the flux\n       * <code>(-f(U_i) - f(U_j) * c_ij</code>\n       */\n      state_type\n      flux_divergence(const flux_contribution_type &flux_i,\n                      const flux_contribution_type &flux_j,\n                      const dealii::Tensor<1, dim, Number> &c_ij) const\n      {\n        return -contract(add(flux_i, flux_j), c_ij);\n      }\n\n      /**\n       * The low-order and high-order fluxes are the same:\n       */\n      static constexpr bool have_high_order_flux = false;\n\n      state_type high_order_flux_divergence(\n          const flux_contribution_type &,\n          const flux_contribution_type &,\n          const dealii::Tensor<1, dim, Number> &) const = delete;\n\n      //@}\n      /**\n       * @name Computing stencil source terms\n       */\n      //@{\n\n      /** We do not have source terms */\n      static constexpr bool have_source_terms = false;\n\n      state_type nodal_source(const PrecomputedVector & /*pv*/,\n                              const unsigned int /*i*/,\n                              const state_type & /*U_i*/,\n                              const ScalarNumber /*tau*/) const = delete;\n\n      state_type nodal_source(const PrecomputedVector & /*pv*/,\n                              const unsigned int * /*js*/,\n                              const state_type & /*U_j*/,\n                              const ScalarNumber /*tau*/) const = delete;\n\n      //@}\n      /**\n       * @name State transformations\n       */\n      //@{\n\n      /**\n       * Given a state vector associated with a different spatial\n       * dimensions than the current one, return an \"expanded\" version of\n       * the state vector associated with @a dim spatial dimensions where\n       * the momentum vector of the conserved state @p state is expaned\n       * with zeros to a total length of @a dim entries.\n       *\n       * @note @a dim has to be larger or equal than the dimension of the\n       * @a ST vector.\n       */\n      template <typename ST>\n      state_type expand_state(const ST &state) const\n      {\n        return state;\n      }\n\n      /**\n       * Given a primitive state [rho, u_1, ..., u_d, p] return a conserved\n       * state\n       */\n      state_type from_primitive_state(const state_type &primitive_state) const\n      {\n        return primitive_state;\n      }\n\n      /**\n       * Given a conserved state return a primitive state [rho, u_1, ..., u_d,\n       * p]\n       */\n      state_type to_primitive_state(const state_type &state) const\n      {\n        return state;\n      }\n\n      /**\n       * Transform the current state according to a  given operator\n       * @p lambda acting on a @a dim dimensional momentum (or velocity)\n       * vector.\n       */\n      template <typename Lambda>\n      state_type apply_galilei_transform(const state_type &state,\n                                         const Lambda & /*lambda*/) const\n      {\n        return state;\n      }\n\n    }; /* HyperbolicSystemView */\n  }    // namespace Skeleton\n} // namespace ryujin\n"
  },
  {
    "path": "source/skeleton/indicator.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"hyperbolic_system.h\"\n\n#include <multicomponent_vector.h>\n#include <simd.h>\n\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/vectorization.h>\n\n\nnamespace ryujin\n{\n  namespace Skeleton\n  {\n    template <typename ScalarNumber = double>\n    class IndicatorParameters : public dealii::ParameterAcceptor\n    {\n    public:\n      IndicatorParameters(const std::string &subsection = \"/Indicator\")\n          : ParameterAcceptor(subsection)\n      {\n      }\n    };\n\n\n    /**\n     * An suitable indicator strategy that is used to form the preliminary\n     * high-order update.\n     *\n     * @ingroup SkeletonEquations\n     */\n    template <int dim, typename Number = double>\n    class Indicator\n    {\n    public:\n      /**\n       * @name Typedefs and constexpr constants\n       */\n      //@{\n\n      using View = HyperbolicSystemView<dim, Number>;\n\n      using ScalarNumber = typename View::ScalarNumber;\n\n      using state_type = typename View::state_type;\n\n      using PrecomputedVector = typename View::PrecomputedVector;\n\n      using Parameters = IndicatorParameters<ScalarNumber>;\n\n      //@}\n      /**\n       * @name Stencil-based computation of indicators\n       *\n       * Intended usage:\n       * ```\n       * Indicator<dim, Number> indicator;\n       * for (unsigned int i = n_internal; i < n_owned; ++i) {\n       *   // ...\n       *   indicator.reset(i, U_i);\n       *   for (unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {\n       *     // ...\n       *     indicator.accumulate(js, U_j, c_ij);\n       *   }\n       *   indicator.alpha(hd_i);\n       * }\n       * ```\n       */\n      //@{\n\n      /**\n       * Constructor taking a HyperbolicSystem instance as argument\n       */\n      Indicator(const HyperbolicSystem &hyperbolic_system,\n                const Parameters &parameters,\n                const PrecomputedVector &precomputed_values)\n          : hyperbolic_system(hyperbolic_system)\n          , parameters(parameters)\n          , precomputed_values(precomputed_values)\n      {\n      }\n\n      /**\n       * Reset temporary storage and initialize for a new row corresponding\n       * to state vector U_i.\n       */\n      void reset(const unsigned int /*i*/, const state_type & /*U_i*/)\n      {\n        // empty\n      }\n\n      /**\n       * When looping over the sparsity row, add the contribution associated\n       * with the neighboring state U_j.\n       */\n      void accumulate(const unsigned int * /*js*/,\n                      const state_type & /*U_j*/,\n                      const dealii::Tensor<1, dim, Number> & /*c_ij*/)\n      {\n        // empty\n      }\n\n      /**\n       * Return the computed alpha_i value.\n       */\n      Number alpha(const Number /*h_i*/) const\n      {\n        return Number(0.);\n      }\n\n      //@}\n\n    private:\n      /**\n       * @name\n       */\n      //@{\n\n      const HyperbolicSystem &hyperbolic_system;\n      const Parameters &parameters;\n      const PrecomputedVector &precomputed_values;\n\n      //@}\n    };\n  } // namespace Skeleton\n} // namespace ryujin\n"
  },
  {
    "path": "source/skeleton/initial_state_library.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 by the ryujin authors\n//\n\n#include \"initial_state_library.template.h\"\n\nnamespace ryujin\n{\n  template class InitialStateLibrary<Description, 1, NUMBER>;\n  template class InitialStateLibrary<Description, 2, NUMBER>;\n  template class InitialStateLibrary<Description, 3, NUMBER>;\n} // namespace ryujin\n"
  },
  {
    "path": "source/skeleton/initial_state_library.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <initial_state_library.h>\n\n#include \"description.h\"\n#include \"initial_state_uniform.h\"\n\nnamespace ryujin\n{\n  using namespace Skeleton;\n\n  template <int dim, typename Number>\n  class InitialStateLibrary<Description, dim, Number>\n  {\n  public:\n    using HyperbolicSystem = typename Description::HyperbolicSystem;\n    using ParabolicSystem = typename Description::ParabolicSystem;\n\n    using View =\n        typename Description::template HyperbolicSystemView<dim, Number>;\n\n    using initial_state_list_type =\n        std::set<std::unique_ptr<InitialState<Description, dim, Number>>>;\n\n    static void\n    populate_initial_state_list(initial_state_list_type &initial_state_list,\n                                const HyperbolicSystem &h,\n                                const ParabolicSystem & /*p*/,\n                                const std::string &s)\n    {\n      auto add = [&](auto &&object) {\n        initial_state_list.emplace(std::move(object));\n      };\n\n      add(std::make_unique<Uniform<dim, Number>>(h, s));\n    }\n  };\n} // namespace ryujin\n"
  },
  {
    "path": "source/skeleton/initial_state_uniform.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2022 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"hyperbolic_system.h\"\n#include <initial_state_library.h>\n\nnamespace ryujin\n{\n  namespace Skeleton\n  {\n    struct Description;\n\n    /**\n     * Uniform initial state defined by a given primitive state.\n     *\n     * @ingroup SkeletonEquations\n     */\n    template <int dim, typename Number>\n    class Uniform : public InitialState<Description, dim, Number>\n    {\n    public:\n      using View = HyperbolicSystemView<dim, Number>;\n      using state_type = typename View::state_type;\n\n      Uniform(const HyperbolicSystem &hyperbolic_system,\n              const std::string subsection)\n          : InitialState<Description, dim, Number>(\"uniform\", subsection)\n          , hyperbolic_system(hyperbolic_system)\n      {\n        primitive_[0] = 1.0;\n        this->add_parameter(\n            \"primitive state\", primitive_, \"Initial 1d primitive state\");\n      }\n\n      state_type compute(const dealii::Point<dim> & /*point*/,\n                         Number /*t*/) final\n      {\n        const auto view = hyperbolic_system.view<dim, Number>();\n        return view.from_primitive_state(view.expand_state(primitive_));\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system;\n\n      state_type primitive_;\n    };\n  } // namespace Skeleton\n} // namespace ryujin\n"
  },
  {
    "path": "source/skeleton/instantiate.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 by the ryujin authors\n//\n\n#ifndef RYUJIN_INCLUDE_INSTANTIATION_ONCE\n#define RYUJIN_INCLUDE_INSTANTIATION_ONCE\n#else\n#error Instantiation files can only be included once.\n#endif\n\n#include \"description.h\"\n\nnamespace ryujin\n{\n  using Skeleton::Description;\n} // namespace ryujin\n"
  },
  {
    "path": "source/skeleton/limiter.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"hyperbolic_system.h\"\n\n#include <compile_time_options.h>\n#include <multicomponent_vector.h>\n#include <newton.h>\n#include <simd.h>\n\nnamespace ryujin\n{\n  namespace Skeleton\n  {\n    template <typename ScalarNumber = double>\n    class LimiterParameters : public dealii::ParameterAcceptor\n    {\n    public:\n      LimiterParameters(const std::string &subsection = \"/Limiter\")\n          : ParameterAcceptor(subsection)\n      {\n        iterations_ = 2;\n        add_parameter(\n            \"iterations\", iterations_, \"Number of limiter iterations\");\n      }\n\n      ACCESSOR_READ_ONLY(iterations);\n\n    private:\n      unsigned int iterations_;\n    };\n\n\n    /**\n     * The convex limiter.\n     *\n     * @ingroup SkeletonEquations\n     */\n    template <int dim, typename Number = double>\n    class Limiter\n    {\n    public:\n      /**\n       * @name Typedefs and constexpr constants\n       */\n      //@{\n\n      using View = HyperbolicSystemView<dim, Number>;\n\n      using ScalarNumber = typename View::ScalarNumber;\n\n      using state_type = typename View::state_type;\n\n      using flux_contribution_type = typename View::flux_contribution_type;\n\n      using PrecomputedVector = typename View::PrecomputedVector;\n\n      using Parameters = LimiterParameters<ScalarNumber>;\n\n      //@}\n      /**\n       * @name Computation and manipulation of bounds\n       */\n      //@{\n      /**\n       * The number of stored entries in the bounds array.\n       */\n      static constexpr unsigned int n_bounds = 0;\n\n      /**\n       * Array type used to store accumulated bounds.\n       */\n      using Bounds = std::array<Number, n_bounds>;\n\n      /**\n       * Constructor taking a HyperbolicSystem instance as argument\n       */\n      Limiter(const HyperbolicSystem &hyperbolic_system,\n              const Parameters &parameters,\n              const PrecomputedVector &precomputed_values)\n          : hyperbolic_system(hyperbolic_system)\n          , parameters(parameters)\n          , precomputed_values(precomputed_values)\n      {\n      }\n\n      /**\n       * Given a state @p U_i and an index @p i return \"strict\" bounds,\n       * i.e., a minimal convex set containing the state.\n       */\n      Bounds projection_bounds_from_state(const unsigned int /*i*/,\n                                          const state_type & /*U_i*/) const\n      {\n        return Bounds{};\n      }\n\n      /**\n       * Given two bounds bounds_left, bounds_right, this function computes\n       * a larger, combined set of bounds that this is a (convex) superset\n       * of the two.\n       */\n      Bounds combine_bounds(const Bounds & /*bounds_left*/,\n                            const Bounds & /*bounds_right*/) const\n      {\n        return Bounds{};\n      }\n\n      /**\n       * This function applies a relaxation to a given a (strict) bound @p\n       * bounds using a non dimensionalized measure @p hd (that should\n       * scale as $h^d$, where $h$ is the local mesh size).\n       */\n      Bounds fully_relax_bounds(const Bounds & /*bounds*/,\n                                const Number & /*hd*/) const\n      {\n        return Bounds{};\n      }\n\n      //@}\n      /**\n       * @name Stencil-based computation of bounds\n       *\n       * Intended usage:\n       * ```\n       * Limiter<dim, Number> limiter;\n       * for (unsigned int i = n_internal; i < n_owned; ++i) {\n       *   // ...\n       *   limiter.reset(i, U_i, flux_i);\n       *   for (unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {\n       *     // ...\n       *     limiter.accumulate(js, U_j, flux_j, scaled_c_ij, affine_shift);\n       *   }\n       *   limiter.bounds(hd_i);\n       * }\n       * ```\n       */\n      //@{\n\n      /**\n       * Reset temporary storage\n       */\n      void reset(const unsigned int /*i*/,\n                 const state_type & /*new_U_i*/,\n                 const flux_contribution_type & /*new_flux_i*/)\n      {\n        // empty\n      }\n\n      /**\n       * When looping over the sparsity row, add the contribution associated\n       * with the neighboring state U_j.\n       */\n      void accumulate(const unsigned int * /*js*/,\n                      const state_type & /*U_j*/,\n                      const flux_contribution_type & /*flux_j*/,\n                      const dealii::Tensor<1, dim, Number> & /*scaled_c_ij*/,\n                      const state_type & /*affine_shift*/)\n      {\n        // empty\n      }\n\n      /**\n       * Return the computed bounds (with relaxation applied).\n       */\n      Bounds bounds(const Number hd_i) const\n      {\n        auto relaxed_bounds = fully_relax_bounds(bounds_, hd_i);\n\n        return relaxed_bounds;\n      }\n\n      //*}\n      /** @name Convex limiter */\n      //@{\n\n      /**\n       * Given a state \\f$\\mathbf U\\f$ and an update \\f$\\mathbf P\\f$ this\n       * function computes and returns the maximal coefficient \\f$t\\f$,\n       * obeying \\f$t_{\\text{min}} < t < t_{\\text{max}}\\f$, such that the\n       * selected local minimum principles are obeyed.\n       */\n      std::tuple<Number, bool> limit(const Bounds & /*bounds*/,\n                                     const state_type & /*U*/,\n                                     const state_type & /*P*/,\n                                     const Number /*t_min*/ = Number(0.),\n                                     const Number t_max = Number(1.)) const\n      {\n        return {t_max, true};\n      }\n\n    private:\n      //@}\n      /** @name Arguments and internal fields */\n      //@{\n\n      const HyperbolicSystem &hyperbolic_system;\n      const Parameters &parameters;\n      const PrecomputedVector &precomputed_values;\n\n      Bounds bounds_;\n      //@}\n    };\n  } // namespace Skeleton\n} // namespace ryujin\n"
  },
  {
    "path": "source/skeleton/riemann_solver.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2023 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"hyperbolic_system.h\"\n\n#include <simd.h>\n\n#include <deal.II/base/point.h>\n#include <deal.II/base/tensor.h>\n\nnamespace ryujin\n{\n  namespace Skeleton\n  {\n    template <typename ScalarNumber = double>\n    class RiemannSolverParameters : public dealii::ParameterAcceptor\n    {\n    public:\n      RiemannSolverParameters(const std::string &subsection = \"/RiemannSolver\")\n          : ParameterAcceptor(subsection)\n      {\n      }\n    };\n\n\n    /**\n     * A fast approximative solver for the associated 1D Riemann problem.\n     * The solver has to ensure that the estimate\n     * \\f$\\lambda_{\\text{max}}\\f$ that is returned for the maximal\n     * wavespeed is a strict upper bound.\n     *\n     * @ingroup SkeletonEquations\n     */\n    template <int dim, typename Number = double>\n    class RiemannSolver\n    {\n    public:\n      /**\n       * @name Typedefs and constexpr constants\n       */\n      //@{\n\n      using View = HyperbolicSystemView<dim, Number>;\n\n      using ScalarNumber = typename View::ScalarNumber;\n\n      using state_type = typename View::state_type;\n\n      using PrecomputedVector = typename View::PrecomputedVector;\n\n      using Parameters = RiemannSolverParameters<ScalarNumber>;\n\n      //@}\n      /**\n       * @name Compute wavespeed estimates\n       */\n      //@{\n\n      /**\n       * Constructor taking a HyperbolicSystem instance as argument\n       */\n      RiemannSolver(const HyperbolicSystem &hyperbolic_system,\n                    const Parameters &parameters,\n                    const PrecomputedVector &precomputed_values)\n          : hyperbolic_system(hyperbolic_system)\n          , parameters(parameters)\n          , precomputed_values(precomputed_values)\n      {\n      }\n\n      /**\n       * For two given states U_i a U_j and a (normalized) \"direction\" n_ij\n       * compute an estimation of an upper bound for lambda.\n       */\n      Number compute(const state_type & /*U_i*/,\n                     const state_type & /*U_j*/,\n                     const unsigned int /*i*/,\n                     const unsigned int * /*js*/,\n                     const dealii::Tensor<1, dim, Number> & /*n_ij*/) const\n      {\n        return Number(std::numeric_limits<ScalarNumber>::epsilon());\n      }\n\n    private:\n      const HyperbolicSystem &hyperbolic_system;\n      const Parameters &parameters;\n      const PrecomputedVector &precomputed_values;\n      //@}\n    };\n  } // namespace Skeleton\n} // namespace ryujin\n"
  },
  {
    "path": "source/solution_transfer.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2024 by the ryujin authors\n//\n\n#include \"solution_transfer.template.h\"\n\n#include <instantiate.h>\n\nnamespace ryujin\n{\n  template class SolutionTransfer<Description, 1, NUMBER>;\n  template class SolutionTransfer<Description, 2, NUMBER>;\n  template class SolutionTransfer<Description, 3, NUMBER>;\n} // namespace ryujin\n"
  },
  {
    "path": "source/solution_transfer.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception or LGPL-2.1-or-later\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"mpi_ensemble.h\"\n#include \"observer_pointer.h\"\n#include \"offline_data.h\"\n#include \"state_vector.h\"\n\n#include <deal.II/base/parameter_acceptor.h>\n\nnamespace ryujin\n{\n  /**\n   * The SolutionTransfer class is an adaptation of the\n   * parallel::distributed::SolutionTransfer class and method implemented\n   * in deal.II. This class is, in contrast to the deal.II version,\n   * specifically taylored to our needs:\n   *  - it uses the MultiComponentVector directly,\n   *  - it implements a local mass matrix projection with convex limiting.\n   *\n   * @ingroup Mesh\n   */\n  template <typename Description, int dim, typename Number = double>\n  class SolutionTransfer : public dealii::ParameterAcceptor\n  {\n  public:\n    /**\n     * @name Typedefs and constexpr constants\n     */\n    //@{\n\n    using HyperbolicSystem = typename Description::HyperbolicSystem;\n    using ParabolicSystem = typename Description::ParabolicSystem;\n\n    using View =\n        typename Description::template HyperbolicSystemView<dim, Number>;\n\n    static constexpr auto problem_dimension = View::problem_dimension;\n\n    using state_type = typename View::state_type;\n\n    using StateVector = typename View::StateVector;\n    using HyperbolicVector = typename View::HyperbolicVector;\n\n    /**\n     * The number of stored entries in the bounds array.\n     */\n    static constexpr unsigned int n_bounds =\n        Description::template Limiter<dim, Number>::n_bounds;\n\n    /**\n     * Array type used to store accumulated bounds.\n     */\n    using Bounds = typename Description::template Limiter<dim, Number>::Bounds;\n\n    //@}\n    /**\n     * @name Constructor and setup\n     */\n    //@{\n\n    /**\n     * Constructor\n     */\n    SolutionTransfer(const MPIEnsemble &mpi_ensemble,\n                     const OfflineData<dim, Number> &offline_data,\n                     const HyperbolicSystem &hyperbolic_system,\n                     const ParabolicSystem &parabolic_system,\n                     const std::string &subsection = \"/SolutionTransfer\");\n\n    /**\n     * Destructor\n     */\n    ~SolutionTransfer() override = default;\n\n    //@}\n    /**\n     * @name Methods for solution transfer after mesh adaptation and\n     * serialization/deserialization.\n     */\n    //@{\n\n    /**\n     * Prepare projection (and serialization) by registering a call back to\n     * all cells of the distributed triangulation. The call back attaches\n     * all necessary data to the triangulation to perform a projection\n     * after mesh adaptation, or a Triangulation::store()/load() operation.\n     */\n    void prepare_projection(const StateVector &old_state_vector);\n\n    /**\n     * Return the handle associated with the call back that was set by\n     * prepare_projection() and the associated data attached to the\n     * triangulation.\n     */\n    unsigned int get_handle() const\n    {\n      Assert(\n          handle_ != dealii::numbers::invalid_unsigned_int,\n          dealii::ExcMessage(\"Invalid handle: Cannot retrieve a valid \"\n                             \"handle. get_handle() can only be called after a \"\n                             \"call to prepare_projection(), or set_handle().\"));\n      return handle_;\n    }\n\n    /**\n     * Set the handle associated data attached to the triangulation. This\n     * function has to be called after a Triangulation::load() operation\n     * and prior to the SolutionTransfer::project().\n     */\n    void set_handle(unsigned int handle)\n    {\n      Assert(\n          handle_ == dealii::numbers::invalid_unsigned_int,\n          dealii::ExcMessage(\"Invalid state: Cannot set handle because we \"\n                             \"already have a valid handle due to a prior call \"\n                             \"to prepare_projection(), or set_handle().\"));\n      handle_ = handle;\n    }\n\n    /**\n     * Return the handle associated with the call back that was set by\n     * prepare_projection() and the associated data attached to the\n     * triangulation.\n     */\n    void reset_handle()\n    {\n      handle_ = dealii::numbers::invalid_unsigned_int;\n    }\n\n    /**\n     * Project the data stored in the triangulation into a new state vector\n     * @p new_state_vector. The attached data either comes from a previous\n     * call to prepare_projection() prior to mesh adaptation or has been\n     * read back in from a checkpoint after a call to\n     * Triangulation::load().\n     *\n     * @note After mesh refinement all internal data structures stored in\n     * the OfflineData object must be reinitialized with a call to\n     * prepare() and the @p new_state_vector must be appropriately\n     * initialized with the new Partitioner.\n     */\n    void project(StateVector &new_state_vector);\n\n  private:\n    //@}\n    /**\n     * @name Run time options\n     */\n    //@{\n\n    typename Description::template Limiter<dim, Number>::Parameters\n        limiter_parameters_;\n\n    //@}\n    /**\n     * @name Internal data\n     */\n    //@{\n\n    const MPIEnsemble &mpi_ensemble_;\n\n    dealii::ObserverPointer<const OfflineData<dim, Number>> offline_data_;\n    dealii::ObserverPointer<const HyperbolicSystem> hyperbolic_system_;\n    dealii::ObserverPointer<const ParabolicSystem> parabolic_system_;\n\n    unsigned int handle_;\n\n    /**\n     * Helper function reading in a state from the hyperbolic state vector.\n     * The function translates the global index @p global_i into the local\n     * range (operated on by this MPI rank) and automatically distributes\n     * values to constrained degrees of freedom.\n     *\n     * @note Ordinarily distribute()ing constrained degrees of freedom\n     * would happen with AffineConstraints<>::distribute() - but this\n     * function is incompatible with our MultiComponentVector.\n     */\n    state_type read_tensor(const HyperbolicVector &U,\n                           const dealii::types::global_dof_index global_i);\n\n    /**\n     * Helper function adding a supplied state to the hyperbolic state\n     * vector. The function translates the global index @p global_i into\n     * the local range (operated on by this MPI rank).\n     */\n    void add_tensor(HyperbolicVector &U,\n                    const state_type &U_i,\n                    const dealii::types::global_dof_index global_i);\n  };\n} // namespace ryujin\n"
  },
  {
    "path": "source/solution_transfer.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception or LGPL-2.1-or-later\n// Copyright (C) 2024 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include \"solution_transfer.h\"\n#if DEAL_II_VERSION_GTE(9, 6, 0)\n#include \"tensor_product_point_kernels.h\"\n#endif\n\n#include <deal.II/base/exceptions.h>\n#include <deal.II/distributed/tria.h>\n#include <deal.II/dofs/dof_accessor.h>\n#include <deal.II/dofs/dof_tools.h>\n#if DEAL_II_VERSION_GTE(9, 6, 0)\n#include <deal.II/grid/cell_status.h>\n#endif\n#include <deal.II/grid/tria_accessor.h>\n#include <deal.II/grid/tria_iterator.h>\n#include <deal.II/lac/block_vector.h>\n#include <deal.II/lac/la_parallel_block_vector.h>\n#include <deal.II/lac/la_parallel_vector.h>\n#include <deal.II/lac/petsc_block_vector.h>\n#include <deal.II/lac/petsc_vector.h>\n#include <deal.II/lac/trilinos_parallel_block_vector.h>\n#include <deal.II/lac/trilinos_vector.h>\n#include <deal.II/lac/vector.h>\n#include <deal.II/matrix_free/fe_point_evaluation.h>\n\nnamespace ryujin\n{\n  template <typename Description, int dim, typename Number>\n  SolutionTransfer<Description, dim, Number>::SolutionTransfer(\n      const MPIEnsemble &mpi_ensemble,\n      const OfflineData<dim, Number> &offline_data,\n      const HyperbolicSystem &hyperbolic_system,\n      const ParabolicSystem &parabolic_system,\n      const std::string &subsection /* = \"/SolutionTransfer\" */)\n      : ParameterAcceptor(subsection)\n      , limiter_parameters_(subsection + \"/mass transfer limiter\")\n      , mpi_ensemble_(mpi_ensemble)\n      , offline_data_(&offline_data)\n      , hyperbolic_system_(&hyperbolic_system)\n      , parabolic_system_(&parabolic_system)\n      , handle_(dealii::numbers::invalid_unsigned_int)\n  {\n  }\n\n\n  namespace\n  {\n    /**\n     * Pack a vector of local state values into a char vector.\n     */\n    template <typename state_type>\n    std::vector<char>\n    pack_state_values(const std::vector<state_type> &state_values)\n    {\n      std::vector<char> buffer(sizeof(state_type) * state_values.size());\n      std::memcpy(buffer.data(), state_values.data(), buffer.size());\n      return buffer;\n    }\n\n\n    /**\n     * Unpack a char vector into a vector of local state values.\n     */\n    template <typename state_type>\n    std::vector<state_type> unpack_state_values(\n        const boost::iterator_range<std::vector<char>::const_iterator>\n            &data_range)\n    {\n      const std::size_t n_bytes = data_range.size();\n      Assert(n_bytes % sizeof(state_type) == 0, dealii::ExcInternalError());\n      std::vector<state_type> state_values(n_bytes / sizeof(state_type));\n      std::memcpy(state_values.data(),\n                  &data_range[0],\n                  state_values.size() * sizeof(state_type));\n      return state_values;\n    }\n  } // namespace\n\n\n  template <typename Description, int dim, typename Number>\n  inline DEAL_II_ALWAYS_INLINE auto\n  SolutionTransfer<Description, dim, Number>::read_tensor(\n      const HyperbolicVector &U, const dealii::types::global_dof_index global_i)\n      -> state_type\n  {\n    const auto &scalar_partitioner = offline_data_->scalar_partitioner();\n    const auto &affine_constraints = offline_data_->affine_constraints();\n    const auto local_i = scalar_partitioner->global_to_local(global_i);\n    if (affine_constraints.is_constrained(global_i)) {\n      state_type result;\n      const auto &line = *affine_constraints.get_constraint_entries(global_i);\n      for (const auto &[global_k, c_k] : line) {\n        const auto local_k = scalar_partitioner->global_to_local(global_k);\n        result += c_k * U.read_tensor(local_k);\n      }\n      return result;\n    } else {\n      return U.read_tensor(local_i);\n    }\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  inline DEAL_II_ALWAYS_INLINE void\n  SolutionTransfer<Description, dim, Number>::add_tensor(\n      HyperbolicVector &U,\n      const state_type &new_U_i,\n      const dealii::types::global_dof_index global_i)\n  {\n    const auto &scalar_partitioner = offline_data_->scalar_partitioner();\n    const auto local_i = scalar_partitioner->global_to_local(global_i);\n    U.add_tensor(new_U_i, local_i);\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void SolutionTransfer<Description, dim, Number>::prepare_projection(\n      const StateVector &old_state_vector [[maybe_unused]])\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout\n        << \"SolutionTransfer<Description, dim, Number>::prepare_projection()\"\n        << std::endl;\n#endif\n\n#if !DEAL_II_VERSION_GTE(9, 6, 0)\n    AssertThrow(\n        false,\n        dealii::ExcMessage(\n            \"The SolutionTransfer class needs deal.II version 9.6.0 or newer\"));\n\n#else\n    const auto &discretization = offline_data_->discretization();\n    auto &triangulation = *discretization.triangulation_; /* writable */\n\n    Assert(handle_ == dealii::numbers::invalid_unsigned_int,\n           dealii::ExcMessage(\n               \"You can only add one solution per SolutionTransfer object.\"));\n\n    /*\n     * -----------------------------------------------------------------------\n     * Cell-level projection to parent cells and packing data:\n     * -----------------------------------------------------------------------\n     */\n\n    handle_ = triangulation.register_data_attach(\n        [this, &old_state_vector](const auto cell,\n                                  const dealii::CellStatus status) {\n          const auto &dof_handler = offline_data_->dof_handler();\n          const auto dof_cell = typename dealii::DoFHandler<dim>::cell_iterator(\n              &cell->get_triangulation(),\n              cell->level(),\n              cell->index(),\n              &dof_handler);\n\n          const auto &scalar_partitioner = offline_data_->scalar_partitioner();\n\n          const auto &U = std::get<0>(old_state_vector);\n          /* precomputed needs to be valid for bounds computation */\n          const auto &precomputed = std::get<1>(old_state_vector);\n\n          using Limiter = typename Description::template Limiter<dim, Number>;\n          const Limiter limiter(\n              *hyperbolic_system_, limiter_parameters_, precomputed);\n\n          /*\n           * Collect state values for packing:\n           */\n\n          const auto n_dofs_per_cell = dof_cell->get_fe().n_dofs_per_cell();\n          std::vector<state_type> state_values(n_dofs_per_cell);\n\n          switch (status) {\n          case dealii::CellStatus::cell_will_persist:\n            [[fallthrough]];\n          case dealii::CellStatus::cell_will_be_refined: {\n            /*\n             * For both cases we need state values from the currently\n             * active cell:\n             */\n\n            Assert(dof_cell->is_active(), dealii::ExcInternalError());\n            std::vector<dealii::types::global_dof_index> dof_indices(\n                n_dofs_per_cell);\n            dof_cell->get_dof_indices(dof_indices);\n\n            std::transform(\n                std::begin(dof_indices),\n                std::end(dof_indices),\n                std::begin(state_values),\n                [&](const auto global_i) { return read_tensor(U, global_i); });\n          } break;\n\n          case dealii::CellStatus::children_will_be_coarsened: {\n            /*\n             * We need to project values from the active child cells up to\n             * the present parent cell that will become active after\n             * coarsening.\n             */\n\n            Assert(dof_cell->has_children(), dealii::ExcInternalError());\n\n            const auto &discretization = offline_data_->discretization();\n            const auto index = dof_cell->active_fe_index();\n            const auto &finite_element = discretization.finite_element()[index];\n            const auto &mapping = discretization.mapping()[index];\n            const auto &quadrature = discretization.quadrature()[index];\n\n            dealii::FEValues<dim> fe_values(\n                mapping,\n                finite_element,\n                quadrature,\n                dealii::update_values | dealii::update_JxW_values |\n                    dealii::update_quadrature_points);\n\n            const auto polynomial_space =\n                dealii::internal::FEPointEvaluation::get_polynomial_space(\n                    finite_element);\n\n            std::vector<dealii::Point<dim, Number>> unit_points(\n                quadrature.size());\n            /*\n             * for Number == float we need a temporary vector for the\n             * transform_points_real_to_unit_cell() function:\n             */\n            std::vector<dealii::Point<dim>> unit_points_temp(\n                std::is_same_v<Number, float> ? quadrature.size() : 0);\n\n            /* Step 1: build up right hand side by iterating over children: */\n\n            std::vector<state_type> state_values_quad(quadrature.size());\n            std::vector<state_type> local_rhs(n_dofs_per_cell);\n\n            std::vector<dealii::types::global_dof_index> dof_indices(\n                n_dofs_per_cell);\n\n            Bounds bounds;\n\n            for (unsigned int child = 0; child < dof_cell->n_children();\n                 ++child) {\n              const auto child_cell = dof_cell->child(child);\n\n              Assert(child_cell->is_active(), dealii::ExcInternalError());\n              Assert(dof_cell->active_fe_index() ==\n                         child_cell->active_fe_index(),\n                     dealii::ExcMessage(\"SolutionTransfer: projection between \"\n                                        \"different FE space is not set up.\"));\n\n              fe_values.reinit(child_cell);\n\n              if constexpr (std::is_same_v<Number, float>) {\n                mapping.transform_points_real_to_unit_cell(\n                    dof_cell,\n                    fe_values.get_quadrature_points(),\n                    unit_points_temp);\n                std::transform(std::begin(unit_points_temp),\n                               std::end(unit_points_temp),\n                               std::begin(unit_points),\n                               [](const auto &x) { return x; });\n              } else {\n                mapping.transform_points_real_to_unit_cell(\n                    dof_cell, fe_values.get_quadrature_points(), unit_points);\n              }\n\n              child_cell->get_dof_indices(dof_indices);\n\n              /* We want a \"left fold first\" for the bounds: */\n              if (child == 0 &&\n                  std::begin(dof_indices) != std::end(dof_indices)) {\n                const auto global_i = dof_indices[0];\n                const auto U_i = read_tensor(U, global_i);\n                const auto local_i =\n                    scalar_partitioner->global_to_local(global_i);\n                bounds = limiter.projection_bounds_from_state(local_i, U_i);\n              }\n\n              for (auto &it : state_values_quad)\n                it = state_type{};\n\n              for (unsigned int i = 0; i < n_dofs_per_cell; ++i) {\n                const auto global_i = dof_indices[i];\n                const auto U_i = read_tensor(U, global_i);\n                const auto local_i =\n                    scalar_partitioner->global_to_local(global_i);\n                const auto bounds_i =\n                    limiter.projection_bounds_from_state(local_i, U_i);\n                bounds = limiter.combine_bounds(bounds, bounds_i);\n\n                for (unsigned int q = 0; q < quadrature.size(); ++q) {\n                  state_values_quad[q] += U_i * fe_values.shape_value(i, q);\n                }\n              }\n\n              for (unsigned int q = 0; q < quadrature.size(); ++q)\n                state_values_quad[q] *= fe_values.JxW(q);\n\n              for (unsigned int q = 0; q < quadrature.size(); ++q) {\n                const unsigned int n_shapes = polynomial_space.size();\n                AssertIndexRange(n_shapes, 10);\n                dealii::ndarray<Number, 10, 2, dim> shapes;\n                // Evaluate 1d polynomials and their derivatives\n                std::array<Number, dim> point;\n                for (unsigned int d = 0; d < dim; ++d)\n                  point[d] = unit_points[q][d];\n                for (unsigned int i = 0; i < n_shapes; ++i)\n                  polynomial_space[i].values_of_array(point, 1, &shapes[i][0]);\n\n                Assert(finite_element.degree == 1, dealii::ExcNotImplemented());\n\n                ryujin::internal::integrate_tensor_product_value<\n                    /*is linear*/ true,\n                    dim,\n                    Number,\n                    state_type>(shapes.data(),\n                                n_shapes,\n                                state_values_quad[q],\n                                local_rhs.data(),\n                                unit_points[q],\n                                true);\n              }\n            }\n\n            /* Step 2: construct inverse mass matrices: */\n\n            fe_values.reinit(dof_cell);\n\n            dealii::FullMatrix<double> mass_inverse(n_dofs_per_cell,\n                                                    n_dofs_per_cell);\n            dealii::Vector<double> lumped_mass(n_dofs_per_cell);\n            dealii::Vector<double> lumped_mass_inverse(n_dofs_per_cell);\n\n            auto total_mass = Number(0.);\n            for (unsigned int i = 0; i < n_dofs_per_cell; ++i) {\n              for (unsigned int j = 0; j < n_dofs_per_cell; ++j) {\n                double sum = 0;\n                for (unsigned int q = 0; q < quadrature.size(); ++q)\n                  sum += fe_values.shape_value(i, q) *\n                         fe_values.shape_value(j, q) * fe_values.JxW(q);\n                mass_inverse(i, j) = sum;\n                lumped_mass(i) += sum;\n              }\n              lumped_mass_inverse(i) = Number(1.) / lumped_mass(i);\n              total_mass += lumped_mass(i);\n            }\n            mass_inverse.gauss_jordan();\n\n            /* Step 3: compute low-order update and P_ij matrix: */\n\n            bounds = limiter.fully_relax_bounds(bounds, total_mass);\n\n            std::vector<state_type> pij_matrix(n_dofs_per_cell *\n                                               n_dofs_per_cell);\n            dealii::FullMatrix<Number> lij_matrix(n_dofs_per_cell,\n                                                  n_dofs_per_cell);\n\n            const auto kappa_inverse = Number(n_dofs_per_cell);\n            const auto kappa = Number(1.) / kappa_inverse;\n\n            for (unsigned int i = 0; i < n_dofs_per_cell; ++i) {\n              const state_type U_i = lumped_mass_inverse(i) * local_rhs[i];\n              state_values[i] = U_i;\n\n              for (unsigned int j = 0; j < n_dofs_per_cell; ++j) {\n                const auto kronecker_ij = Number(i == j ? 1. : 0.);\n                const auto b_ij =\n                    lumped_mass(i) * mass_inverse(i, j) - kronecker_ij;\n                const auto b_ji =\n                    lumped_mass(j) * mass_inverse(i, j) - kronecker_ij;\n                const auto P_ij = kappa_inverse * lumped_mass_inverse(i) *\n                                  (b_ij * local_rhs[j] - b_ji * local_rhs[i]);\n                pij_matrix[n_dofs_per_cell * i + j] = P_ij;\n              }\n            }\n\n            /* Step 4: compute l_ij matrix and apply limited update: */\n\n            const auto n_iterations = limiter_parameters_.iterations();\n            for (unsigned int pass = 0; pass < n_iterations; ++pass) {\n\n              for (unsigned int i = 0; i < n_dofs_per_cell; ++i) {\n                const auto &U_i = state_values[i];\n\n                for (unsigned int j = 0; j < n_dofs_per_cell; ++j) {\n                  const auto &P_ij = pij_matrix[n_dofs_per_cell * i + j];\n                  const auto &[l_ij, check] = limiter.limit(bounds, U_i, P_ij);\n                  lij_matrix(i, j) = l_ij;\n                }\n              }\n\n              for (unsigned int i = 0; i < n_dofs_per_cell; ++i) {\n                auto &U_i = state_values[i];\n\n                for (unsigned int j = 0; j < n_dofs_per_cell; ++j) {\n                  const auto l_ij =\n                      std::min(lij_matrix(i, j), lij_matrix(j, i));\n                  auto &P_ij = pij_matrix[n_dofs_per_cell * i + j];\n                  U_i += kappa * l_ij * P_ij;\n                  P_ij -= l_ij * P_ij;\n                }\n\n#ifdef DEBUG_EXPENSIVE_BOUNDS_CHECK\n                const auto view =\n                    hyperbolic_system_->template view<dim, Number>();\n                AssertThrow(\n                    view.is_admissible(U_i),\n                    dealii::ExcMessage(\n                        \"Error: inadmissible state encountered in \"\n                        \"register_data_attach / children_will_be_coarsened\"));\n#endif\n              }\n            }\n          } break;\n\n          case dealii::CellStatus::cell_invalid:\n            Assert(false, dealii::ExcInternalError());\n            __builtin_trap();\n            break;\n          }\n\n          return pack_state_values(state_values);\n        },\n        /* returns_variable_size_data =*/false);\n#endif\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void SolutionTransfer<Description, dim, Number>::project(\n      StateVector &new_state_vector [[maybe_unused]])\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"SolutionTransfer<Description, dim, Number>::project()\"\n              << std::endl;\n#endif\n\n#if !DEAL_II_VERSION_GTE(9, 6, 0)\n    AssertThrow(\n        false,\n        dealii::ExcMessage(\n            \"The SolutionTransfer class needs deal.II version 9.6.0 or newer\"));\n\n#else\n\n    const auto &discretization = offline_data_->discretization();\n    auto &triangulation = *discretization.triangulation_; /* writable */\n\n    Assert(\n        handle_ != dealii::numbers::invalid_unsigned_int,\n        dealii::ExcMessage(\n            \"Cannot project() a state vector without valid handle. \"\n            \"prepare_projection() or set_handle() have to be called first.\"));\n\n    const auto &scalar_partitioner = offline_data_->scalar_partitioner();\n    const auto &affine_constraints = offline_data_->affine_constraints();\n    const auto n_locally_owned = offline_data_->n_locally_owned();\n\n    using ScalarHostVector = Vectors::ScalarHostVector<Number>;\n    ScalarHostVector projected_mass;\n    projected_mass.reinit(offline_data_->scalar_partitioner());\n    HyperbolicVector projected_state;\n    projected_state.reinit_with_vector_partitioner(\n        offline_data_->hyperbolic_vector_partitioner());\n\n    /*\n     * We only need to construct entries in a pik_matrix for a small subset\n     * of affected degrees of freedom for which we have to construct the\n     * entire pik_matrix first for the limiting process (in contrast to the\n     * entirely cell-local limiting done before). Let's simply use a map.\n     */\n    std::map<std::tuple<unsigned int /*i*/, unsigned int /*k*/>, state_type>\n        pik_matrix;\n    std::map<unsigned int /*i*/, Bounds> bounds_map;\n\n    ScalarHostVector kappa;\n    kappa.reinit(offline_data_->scalar_partitioner());\n\n    /*\n     * -----------------------------------------------------------------------\n     * Unpacking data and cell-level interpolation/projection to child cells:\n     * -----------------------------------------------------------------------\n     */\n\n    triangulation.notify_ready_to_unpack( //\n        handle_,\n        [this, &projected_mass, &projected_state](\n            const auto &cell,\n            const dealii::CellStatus status,\n            const auto &data_range) {\n          const auto &dof_handler = offline_data_->dof_handler();\n          const auto dof_cell = typename dealii::DoFHandler<dim>::cell_iterator(\n              &cell->get_triangulation(),\n              cell->level(),\n              cell->index(),\n              &dof_handler);\n\n          /*\n           * Retrieve packed values and project onto cell:\n           */\n\n          const auto n_dofs_per_cell = dof_cell->get_fe().n_dofs_per_cell();\n          std::vector<dealii::types::global_dof_index> dof_indices(\n              n_dofs_per_cell);\n\n          const auto state_values = unpack_state_values<state_type>(data_range);\n\n          switch (status) {\n          case dealii::CellStatus::cell_will_persist:\n            [[fallthrough]];\n          case dealii::CellStatus::children_will_be_coarsened: {\n            /*\n             * For both cases we distribute stored state_values to the\n             * projected_state and projected_mass vectors.\n             */\n\n            Assert(dof_cell->is_active(), dealii::ExcInternalError());\n            dof_cell->get_dof_indices(dof_indices);\n\n            const auto &discretization = offline_data_->discretization();\n            const auto index = dof_cell->active_fe_index();\n            const auto &finite_element = discretization.finite_element()[index];\n            const auto &mapping = discretization.mapping()[index];\n            const auto &quadrature = discretization.quadrature()[index];\n\n            dealii::FEValues<dim> fe_values(mapping,\n                                            finite_element,\n                                            quadrature,\n                                            dealii::update_values |\n                                                dealii::update_JxW_values);\n\n            fe_values.reinit(dof_cell);\n\n            dealii::Vector<double> mi(n_dofs_per_cell);\n            for (unsigned int i = 0; i < n_dofs_per_cell; ++i) {\n              double sum = 0;\n              for (unsigned int q = 0; q < quadrature.size(); ++q)\n                sum += fe_values.shape_value(i, q) * fe_values.JxW(q);\n              mi(i) += sum;\n            }\n\n            for (unsigned int i = 0; i < n_dofs_per_cell; ++i) {\n              const auto global_i = dof_indices[i];\n              add_tensor(projected_state, mi(i) * state_values[i], global_i);\n              projected_mass(global_i) += mi(i);\n            }\n\n          } break;\n\n          case dealii::CellStatus::cell_will_be_refined: {\n            /*\n             * We are on a (non active) cell that has been refined. Project\n             * onto the children and do a local mass projection there:\n             */\n\n            Assert(dof_cell->has_children(), dealii::ExcInternalError());\n\n            const auto &discretization = offline_data_->discretization();\n            const auto index = dof_cell->active_fe_index();\n            const auto &finite_element = discretization.finite_element()[index];\n            const auto &mapping = discretization.mapping()[index];\n            const auto &quadrature = discretization.quadrature()[index];\n\n            dealii::FEValues<dim> fe_values(\n                mapping,\n                finite_element,\n                quadrature,\n                dealii::update_values | dealii::update_JxW_values |\n                    dealii::update_quadrature_points);\n\n            const auto polynomial_space =\n                dealii::internal::FEPointEvaluation::get_polynomial_space(\n                    finite_element);\n            std::vector<dealii::Point<dim, Number>> unit_points(\n                quadrature.size());\n            /*\n             * for Number == float we need a temporary vector for the\n             * transform_points_real_to_unit_cell() function:\n             */\n            std::vector<dealii::Point<dim>> unit_points_temp(\n                std::is_same_v<Number, float> ? quadrature.size() : 0);\n\n            dealii::FullMatrix<double> mass_inverse(n_dofs_per_cell,\n                                                    n_dofs_per_cell);\n            dealii::Vector<double> lumped_mass(n_dofs_per_cell);\n            std::vector<state_type> local_rhs(n_dofs_per_cell);\n\n            for (unsigned int child = 0; child < dof_cell->n_children();\n                 ++child) {\n              const auto child_cell = dof_cell->child(child);\n\n              Assert(child_cell->is_active(), dealii::ExcInternalError());\n              Assert(dof_cell->active_fe_index() ==\n                         child_cell->active_fe_index(),\n                     dealii::ExcMessage(\"SolutionTransfer: projection between \"\n                                        \"different FE space is not set up.\"));\n\n              child_cell->get_dof_indices(dof_indices);\n\n              /* Step 1: build up right hand side on child cell: */\n\n              fe_values.reinit(child_cell);\n\n              if constexpr (std::is_same_v<Number, float>) {\n                mapping.transform_points_real_to_unit_cell(\n                    dof_cell,\n                    fe_values.get_quadrature_points(),\n                    unit_points_temp);\n                std::transform(std::begin(unit_points_temp),\n                               std::end(unit_points_temp),\n                               std::begin(unit_points),\n                               [](const auto &x) { return x; });\n              } else {\n                mapping.transform_points_real_to_unit_cell(\n                    dof_cell, fe_values.get_quadrature_points(), unit_points);\n              }\n\n              for (auto &it : local_rhs)\n                it = state_type{};\n\n              for (unsigned int q = 0; q < quadrature.size(); ++q) {\n                Assert(finite_element.degree == 1, dealii::ExcNotImplemented());\n                auto coefficient =\n                    dealii::internal::evaluate_tensor_product_value(\n                        polynomial_space,\n                        make_const_array_view(state_values),\n                        unit_points[q],\n                        /*is linear*/ true);\n                coefficient *= fe_values.JxW(q);\n\n                for (unsigned int i = 0; i < n_dofs_per_cell; ++i)\n                  local_rhs[i] += coefficient * fe_values.shape_value(i, q);\n              }\n\n              /* Step 2: solve with inverse mass matrix on child cell: */\n\n              mass_inverse = Number(0.);\n              lumped_mass = Number(0.);\n              for (unsigned int i = 0; i < n_dofs_per_cell; ++i) {\n                for (unsigned int j = 0; j < n_dofs_per_cell; ++j) {\n                  double sum = 0;\n                  for (unsigned int q = 0; q < quadrature.size(); ++q)\n                    sum += fe_values.shape_value(i, q) *\n                           fe_values.shape_value(j, q) * fe_values.JxW(q);\n                  mass_inverse(i, j) = sum;\n                  lumped_mass(i) += sum;\n                }\n              }\n              mass_inverse.gauss_jordan();\n\n              /* Step 3: compute high order update and write back: */\n\n              for (unsigned int i = 0; i < n_dofs_per_cell; ++i) {\n                state_type U_i;\n                for (unsigned int j = 0; j < n_dofs_per_cell; ++j) {\n                  U_i += mass_inverse(i, j) * local_rhs[j];\n                }\n\n#ifdef DEBUG_EXPENSIVE_BOUNDS_CHECK\n                const auto view =\n                    hyperbolic_system_->template view<dim, Number>();\n                AssertThrow(view.is_admissible(U_i),\n                            dealii::ExcMessage(\n                                \"Error: inadmissible state encountered in \"\n                                \"ready_to_unpack / cell_will_be_refined\"));\n#endif\n                const auto global_i = dof_indices[i];\n                add_tensor(projected_state, lumped_mass(i) * U_i, global_i);\n                projected_mass(global_i) += lumped_mass(i);\n              }\n            } /*child*/\n\n          } break;\n\n          case dealii::CellStatus::cell_invalid:\n            Assert(false, dealii::ExcInternalError());\n            __builtin_trap();\n            break;\n          }\n        });\n\n    projected_mass.compress(dealii::VectorOperation::add);\n    projected_state.compress(dealii::VectorOperation::add);\n\n    /*\n     * -----------------------------------------------------------------------\n     * Redistribute masses to satisfy hanging-node constraints:\n     *\n     * Now redistribute the mass defect introduced by constrained degrees\n     * of freedom. This mostly affects hanging nodes neighboring a\n     * coarsened cell. Here, cell-wise mass projection might lead to a\n     * value for a constrained degree of freedom that differs from the\n     * algebraic relationship expressed by our affine constraints. Thus, we\n     * first compute the defect and then we redistribute it to all degrees\n     * of freedom on the constraint line.\n     * -----------------------------------------------------------------------\n     */\n\n    auto &new_U = std::get<0>(new_state_vector);\n\n    /*\n     * A small lambda that takes the weighted average of all degrees of\n     * freedom, and stores the result in new_U:\n     */\n    const auto update_new_state_vector = [&]() {\n      for (unsigned int local_i = 0; local_i < n_locally_owned; ++local_i) {\n\n        const auto mU_i = projected_state.read_tensor(local_i);\n        const auto m_i = projected_mass.local_element(local_i);\n\n#ifdef DEBUG_EXPENSIVE_BOUNDS_CHECK\n        const auto view = hyperbolic_system_->template view<dim, Number>();\n        AssertThrow(\n            view.is_admissible(mU_i / m_i),\n            dealii::ExcMessage(\"Error: inadmissible state encountered in \"\n                               \"update_new_state_vector()\"));\n#endif\n\n        new_U.write_tensor(mU_i / m_i, local_i);\n      }\n      new_U.update_ghost_values();\n    };\n\n    update_new_state_vector();\n\n    auto &precomputed = std::get<1>(new_state_vector);\n\n    /* The limiter requires valid precomputed values. Therefore, update: */\n    const auto update_precomputed_values = [&]() {\n      new_U.update_ghost_values();\n      hyperbolic_system_->fill_precomputed_values(\n          *offline_data_, new_state_vector, /*skip_constrainted_dofs*/ false);\n      precomputed.update_ghost_values();\n    };\n\n    update_precomputed_values();\n\n    using Limiter = typename Description::template Limiter<dim, Number>;\n    const Limiter limiter(\n        *hyperbolic_system_, limiter_parameters_, precomputed);\n\n    /*\n     * Step 1: compute low-order update P_ij matrix, and bounds:\n     *\n     * We compute limiter bounds as a single value over the constraint\n     * line. This makes sense as we need to limit the update for each\n     * affected (unconstrained) degree of freedom of a constraint line with\n     * a single limiter value anyway to ensure mass conservation.\n     * (Incidentally, this avoids having to update a global, distributed\n     * bounds vector over all MPI ranks.)\n     */\n\n    for (const auto &line : affine_constraints.get_lines()) {\n      const auto global_i = line.index;\n      const auto local_i = scalar_partitioner->global_to_local(global_i);\n\n      /* Only operate on a locally owned, constrained degree of freedom: */\n      if (local_i >= n_locally_owned)\n        continue;\n\n      /* The result of the mass projection: */\n      const auto m_i_star = projected_mass.local_element(local_i);\n      const auto U_i_star = projected_state.read_tensor(local_i) / m_i_star;\n\n      auto &bounds = bounds_map[local_i]; /* by reference */\n      bounds = limiter.projection_bounds_from_state(local_i, U_i_star);\n\n      /* The value obtained from the affine constraints object: */\n      state_type U_i_interp;\n      for (const auto &[global_k, c_k] : line.entries) {\n        const auto local_k = scalar_partitioner->global_to_local(global_k);\n        U_i_interp += c_k * new_U.read_tensor(local_k);\n      }\n\n      /* And redistribute low order update: */\n      for (const auto &[global_k, c_k] : line.entries) {\n        const auto local_k = scalar_partitioner->global_to_local(global_k);\n        const auto U_k = new_U.read_tensor(local_k);\n\n        const auto bounds_k =\n            limiter.projection_bounds_from_state(local_k, U_k);\n        bounds = limiter.combine_bounds(bounds, bounds_k);\n\n        projected_state.add_tensor(c_k * m_i_star * U_i_star, local_k);\n        projected_mass.local_element(local_k) += c_k * m_i_star;\n\n        kappa.local_element(local_k) += Number(1.);\n        pik_matrix[{local_i, local_k}] = c_k * m_i_star * (U_k - U_i_interp);\n      }\n    }\n\n    /* Compress vectors, recalculate unconstrained states: */\n    projected_mass.compress(dealii::VectorOperation::add);\n    projected_state.compress(dealii::VectorOperation::add);\n    kappa.compress(dealii::VectorOperation::add);\n    update_new_state_vector();\n\n    /* Redistribute ghost layer for masses and kappa: */\n    projected_mass.update_ghost_values();\n    kappa.update_ghost_values();\n\n    /* Step 2: Apply limiter: */\n\n    const auto n_iterations = limiter_parameters_.iterations();\n    for (unsigned int pass = 0; pass < n_iterations; ++pass) {\n\n      /* Update precomputed values for bounds correction: */\n      update_precomputed_values();\n\n      for (const auto &line : affine_constraints.get_lines()) {\n        const auto global_i = line.index;\n        const auto local_i = scalar_partitioner->global_to_local(global_i);\n\n        /* Only operate on a locally owned, constrained degree of freedom: */\n        if (local_i >= n_locally_owned)\n          continue;\n\n        /*\n         * We are computing bounds only over a local constraint line\n         * without recombining such bounds per (unconstrained) degree of\n         * freedom globally. We avoid doing the latter because it would\n         * require a custom \"VectorOperation\" invoking\n         * Limiter::combine_bounds(), which we currently do not have at our\n         * disposal.\n         *\n         * As a simple workaround we simply recompute bounds for the\n         * constraint line after the low-order update and each limiter pass\n         * and recombine those into the stored value.\n         */\n\n        auto &bounds = bounds_map[local_i]; /* by reference */\n        auto total_mass = Number(0.);\n        for (const auto &[global_k, c_k] : line.entries) {\n          const auto local_k = scalar_partitioner->global_to_local(global_k);\n          const auto U_k = new_U.read_tensor(local_k);\n          const auto bounds_k =\n              limiter.projection_bounds_from_state(local_k, U_k);\n          bounds = limiter.combine_bounds(bounds, bounds_k);\n\n          const auto m_k = projected_mass.local_element(local_k);\n          total_mass += m_k;\n        }\n\n        auto l = Number(1.);\n\n        /* Apply relaxation: */\n        const auto relaxed_bounds =\n            limiter.fully_relax_bounds(bounds, total_mass);\n\n        /* Compute limiter values: */\n\n        for (const auto &[global_k, c_k] : line.entries) {\n          const auto local_k = scalar_partitioner->global_to_local(global_k);\n          const auto kappa_k = kappa.local_element(local_k);\n          const auto m_k = projected_mass.local_element(local_k);\n          const auto U_k = new_U.read_tensor(local_k);\n          const auto P_ik = pik_matrix[{local_i, local_k}] * kappa_k / m_k;\n\n          const auto &[l_k, check] = limiter.limit(relaxed_bounds, U_k, P_ik);\n          l = std::min(l, l_k);\n        }\n\n        /* Apply limiter values: */\n\n        for (const auto &[global_k, c_k] : line.entries) {\n          const auto local_k = scalar_partitioner->global_to_local(global_k);\n          auto &mP_ik = pik_matrix[{local_i, local_k}];\n          projected_state.add_tensor(l * mP_ik, local_k);\n          mP_ik -= l * mP_ik;\n        }\n      }\n\n      /* Compress state vector, recalculate unconstrained states: */\n      projected_state.compress(dealii::VectorOperation::add);\n      update_new_state_vector();\n    }\n\n    /* Zero out constrained degrees of freedom: */\n    for (unsigned int local_i = 0; local_i < n_locally_owned; ++local_i) {\n      const auto global_i = scalar_partitioner->local_to_global(local_i);\n      if (affine_constraints.is_constrained(global_i))\n        new_U.write_tensor(state_type{}, local_i);\n    }\n    new_U.update_ghost_values();\n\n#ifdef DEBUG_SYMMETRY_CHECK\n    /*\n     * Sanity check: Final masses must agree:\n     */\n    const auto &lumped_mass_matrix = offline_data_->lumped_mass_matrix();\n    for (unsigned int local_i = 0; local_i < n_locally_owned; ++local_i) {\n      const auto global_i = scalar_partitioner->local_to_global(local_i);\n      if (affine_constraints.is_constrained(global_i))\n        continue;\n\n      const auto m_i = projected_mass.local_element(local_i);\n      const auto m_i_reference = lumped_mass_matrix.read_entry(local_i);\n      Assert(std::abs(m_i - m_i_reference) < 1.e-10,\n             dealii::ExcMessage(\n                 \"SolutionTransfer::projection(): something went wrong. Final \"\n                 \"masses do not agree with those computed in OfflineData.\"));\n    }\n#endif\n#endif\n  }\n} // namespace ryujin\n"
  },
  {
    "path": "source/sparse_matrix.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"sparsity_pattern.h\"\n\n#include <deal.II/base/exceptions.h>\n#include <deal.II/base/partitioner.h>\n#include <deal.II/lac/affine_constraints.h>\n#include <deal.II/lac/dynamic_sparsity_pattern.h>\n\n#include <type_traits>\n\n// #define DEBUG_MPI_EXCHANGE\n\n#ifdef DEBUG_MPI_EXCHANGE\n#include <chrono>\n#endif\n\nnamespace ryujin\n{\n  template <typename Number,\n            int n_comp,\n            int simd_length,\n            typename MemorySpace = dealii::MemorySpace::Host,\n            bool writable = true>\n  class SparseMatrixView;\n\n  /**\n   * A specialized sparse matrix for efficient vectorized SIMD access.\n   *\n   * In the vectorized row index region [0, n_internal_dofs) we store data\n   * as an array-of-struct-of-array type (see the documentation of class\n   * SparsityPattern for details). For the non-vectorized row index\n   * region [n_internal_dofs, n_locally_relevant_dofs) we store the matrix in\n   * CSR format (equivalent to the static dealii::SparsityPattern).\n   */\n  template <typename Number,\n            int n_comp = 1,\n            int simd_length = dealii::VectorizedArray<Number>::size()>\n  class SparseMatrix : public SparseMatrixView<Number, n_comp, simd_length>\n  {\n  public:\n    /**\n     * Constructor and initialization (in host memory space):\n     */\n    //@{\n\n    /**\n     * Default constructor.\n     */\n    SparseMatrix() = default;\n\n    /**\n     * Constructor taking a SIMD sparsity pattern as an argument.\n     */\n    SparseMatrix(const SparsityPattern<simd_length> &sparsity);\n\n    /**\n     * Reinit function reinitializes the matrix with the given SIMD\n     * sparsity pattern. The locally owned and ghost ranges are zeroed.\n     */\n    void reinit(const SparsityPattern<simd_length> &sparsity);\n\n    /**\n     * Return the underlying sparsity pattern.\n     */\n    ACCESSOR_READ_ONLY(sparsity_pattern);\n\n    //@}\n    /**\n     * Memory space access and synchronization:\n     */\n    //@{\n\n    /**\n     * Return a writable view on the sparse matrix for the selected memory\n     * space.\n     */\n    template <typename MemorySpace = dealii::MemorySpace::Host>\n    SparseMatrixView<Number, n_comp, simd_length, MemorySpace, true> get_view();\n\n    /**\n     * Return a read-only view on the sparse matrix for the selected memory\n     * space.\n     */\n    template <typename MemorySpace = dealii::MemorySpace::Host>\n    SparseMatrixView<Number, n_comp, simd_length, MemorySpace, false>\n    get_view() const;\n\n    /**\n     * Returns true if the templated memory space is the currently active\n     * memory space.\n     */\n    template <typename MemorySpace>\n    bool is_active_memory_space() const;\n\n    /**\n     * Move internal data from the currently active memory space to the\n     * templated memory space.\n     */\n    template <typename MemorySpace>\n    void move_to_memory_space();\n\n    //@}\n    /**\n     * MPI synchronization.\n     */\n    //@{\n\n    /**\n     * MPI synchronization: Zero out all ghost rows.\n     */\n    template <typename MemorySpace>\n    void zero_out_ghost_rows_on_memory_space();\n\n    /**\n     * MPI synchronization: Import all ghost rows from neighboring MPI\n     * ranks on the templated memory space.\n     */\n    template <typename MemorySpace>\n    void update_ghost_rows_on_memory_space();\n\n    /**\n     * MPI synchronization: Copy the data that has accumulated in the ghost\n     * range to the owning processor. This function operates on the\n     * templated memory space.\n     */\n    template <typename MemorySpace>\n    void compress_on_memory_space(dealii::VectorOperation::values operation);\n\n  private:\n    //@}\n    /**\n     * @name Internal fields, methods, and friends\n     */\n    //@{\n\n    const SparsityPattern<simd_length> *sparsity_pattern_ =\n        nullptr; // FIXME shared_ptr\n\n    using KokkosHost = dealii::MemorySpace::Host::kokkos_space;\n    Kokkos::View<Number *, KokkosHost> data_host_;\n    Kokkos::View<Number *, KokkosHost> exchange_buffer_host_;\n\n    using KokkosDefault = dealii::MemorySpace::Default::kokkos_space;\n    Kokkos::View<Number *, KokkosDefault> data_default_;\n    Kokkos::View<Number *, KokkosDefault> exchange_buffer_default_;\n\n    bool host_space_active_ = true;\n\n    std::vector<MPI_Request> requests_;\n\n    template <typename, int, int, typename, bool>\n    friend class SparseMatrixView;\n\n    //@}\n  };\n\n\n  /**\n   * This class models a \"view\" of the sparse matrix that lives in the host\n   * or device memory space. It provides a number of methods for reading\n   * and writing matrix entries.\n   *\n   * @note This class is designed to be captured by value in computation\n   * loops with access to either the host or device memory space. As such\n   * we do not store a reference to the underlying SparsityPattern but\n   * rather raw pointers into the corresponding memory. The view is only\n   * valid as long as the underlying SparsityPattern object is not\n   * modified.\n   */\n  template <typename Number,\n            int n_comp,\n            int simd_length,\n            typename MemorySpace,\n            bool writable>\n  class SparseMatrixView\n  {\n  public:\n    SparseMatrixView() = default;\n\n    SparseMatrixView(SparseMatrix<Number, n_comp, simd_length> &sparse_matrix)\n      requires(writable);\n\n    SparseMatrixView(\n        const SparseMatrix<Number, n_comp, simd_length> &sparse_matrix)\n      requires(!writable);\n\n    template <typename SparseMatrix>\n    void reinit(SparseMatrix &sparse_matrix)\n      requires(writable != std::is_const_v<SparseMatrix>);\n\n    /**\n     * Return the underlying sparsity pattern view.\n     */\n    ACCESSOR_READ_ONLY(sparsity_pattern);\n\n    /* Get scalar or tensor-valued entry: */\n\n    /**\n     * Return the (scalar) entry indexed by @p row and @p column_index.\n     *\n     * @note If the template argument @a Number2\n     * is a vectorized array a specialized, faster access will be performed.\n     * In this case the index @p row must be within the interval\n     * [0, n_internal_dofs) and must be divisible by simd_length.\n     *\n     * @note This function is only available if `n_comp` is equal to 1.\n     */\n    template <typename Number2 = Number>\n    DEAL_II_HOST_DEVICE Number2\n    read_entry(const unsigned int row, const unsigned int column_index) const;\n\n    /**\n     * Return the tensor-valued entry indexed by @p row and\n     * @p column_index. This function performs the same operation\n     * as read_entry() except that it always returns the entry as a tensor\n     * (even if it is effectively a scalar entry).\n     *\n     * @note If the template argument @a Number2\n     * is a vectorized array a specialized, faster access will be performed.\n     * In this case the index @p row must be within the interval\n     * [0, n_internal_dofs) and must be divisible by simd_length.\n     */\n    template <typename Number2 = Number,\n              typename Tensor = dealii::Tensor<1, n_comp, Number2>>\n    DEAL_II_HOST_DEVICE Tensor\n    read_tensor(const unsigned int row, const unsigned int column_index) const;\n\n    /* Get transposed scalar or tensor-valued entry: */\n\n    /**\n     * Return the transposed (sclar) entry indexed by @p row and\n     * @p column_index.\n     *\n     * @note If the template argument @a Number2\n     * is a vectorized array a specialized, faster access will be performed.\n     * In this case the index @p row must be within the interval\n     * [0, n_internal_dofs) and must be divisible by simd_length.\n     *\n     * @note This function is only available if `n_comp` is equal to 1.\n     */\n    template <typename Number2 = Number>\n    DEAL_II_HOST_DEVICE Number2 read_transposed_entry(\n        const unsigned int row, const unsigned int column_index) const;\n\n    /**\n     * Return the transposed tensor-valued entry indexed by @p row and\n     * @a column_index. This function performs the same operation\n     * as read_entry() except that it always returns the entry as a tensor\n     * (even if it is effectively a scalar entry).\n     *\n     * @note If the template argument @a Number2\n     * is a vectorized array a specialized, faster access will be performed.\n     * In this case the index @p row must be within the interval\n     * [0, n_internal_dofs) and must be divisible by simd_length.\n     */\n    template <typename Number2 = Number,\n              typename Tensor = dealii::Tensor<1, n_comp, Number2>>\n    DEAL_II_HOST_DEVICE Tensor read_transposed_tensor(\n        const unsigned int row, const unsigned int column_index) const;\n\n    /* Write scalar or tensor entry: */\n\n    /**\n     * Write a (scalar-valued) @p entry to the matrix indexed by @p row\n     * and @p column_index.\n     *\n     * @note If the template argument @a Number2\n     * is a vectorized array a specialized, faster access will be performed.\n     * In this case the index @p row must be within the interval\n     * [0, n_internal_dofs) and must be divisible by simd_length.\n     *\n     * @note This function is only available if `n_comp` is equal to 1.\n     */\n    template <typename Number2 = Number>\n    DEAL_II_HOST_DEVICE void\n    write_entry(const Number2 entry,\n                const unsigned int row,\n                const unsigned int column_index,\n                const bool do_streaming_store = false) const\n      requires(writable);\n\n    /**\n     * Write a tensor-valued @p entry to the matrix indexed by @p row\n     * and @p column_index.\n     *\n     * @note If the template argument @a Number2\n     * is a vectorized array a specialized, faster access will be performed.\n     * In this case the index @p row must be within the interval\n     * [0, n_internal_dofs) and must be divisible by simd_length.\n     */\n    template <typename Number2 = Number,\n              typename Tensor = dealii::Tensor<1, n_comp, Number2>>\n    DEAL_II_HOST_DEVICE void\n    write_tensor(const Tensor &tensor,\n                 const unsigned int row,\n                 const unsigned int column_index,\n                 const bool do_streaming_store = false) const\n      requires(writable);\n\n    /**\n     * Add a (scalar-valued) @p entry to the matrix indexed by @p row\n     * and @p column_index.\n     *\n     * @note If the template argument @a Number2\n     * is a vectorized array a specialized, faster access will be performed.\n     * In this case the index @p row must be within the interval\n     * [0, n_internal_dofs) and must be divisible by simd_length.\n     *\n     * @note This function is only available if `n_comp` is equal to 1.\n     */\n    template <typename Number2 = Number>\n    DEAL_II_HOST_DEVICE void add_entry(const Number2 entry,\n                                       const unsigned int row,\n                                       const unsigned int column_index) const\n      requires(writable);\n\n    /**\n     * Add a tensor-valued @p entry to the matrix indexed by @p row and @p\n     * column_index.\n     *\n     * @note If the template argument @a Number2\n     * is a vectorized array a specialized, faster access will be performed.\n     * In this case the index @p row must be within the interval\n     * [0, n_internal_dofs) and must be divisible by simd_length.\n     */\n    template <typename Number2 = Number,\n              typename Tensor = dealii::Tensor<1, n_comp, Number2>>\n    DEAL_II_HOST_DEVICE void add_tensor(const Tensor &tensor,\n                                        const unsigned int row,\n                                        const unsigned int column_index) const\n      requires(writable);\n\n\n    //@}\n    /**\n     * MPI synchronization.\n     */\n    //@{\n\n    void zero_out_ghost_rows() const\n      requires(writable);\n\n    void update_ghost_rows() const\n      requires(writable);\n\n    void compress(dealii::VectorOperation::values operation) const\n      requires(writable);\n\n    //@}\n\n  private:\n    using SM = SparseMatrix<Number, n_comp, simd_length>;\n    std::conditional_t<writable, SM *, const SM *> sparse_matrix_;\n\n    SparsityPatternView<simd_length, MemorySpace> sparsity_pattern_;\n\n    using KokkosSpace = typename MemorySpace::kokkos_space;\n    Kokkos::View<Number *, KokkosSpace> data_;\n  };\n\n\n  /*\n   * Given a matrix contribution and row and column indices in (deal.II\n   * typical) global numbering, add the contribution to the sparse matrix.\n   * The function takes an @p affine_constraints object in  (deal.II\n   * typical) global numbering and resolves constrained degrees of freedom\n   * prior to distributing to the matrix.\n   *\n   * @note The method will not modify the diagonal entry of constrained\n   * degrees of freedom, in contrast to the deal.II version\n   * AffineConstraints<Number>::distribute_local_to_global().\n   *\n   * @note For a vector-valued matrix with n_comp > 1 the @p cell_matrix\n   * must be a container with a subscript operator[] returning a matrix for\n   * each component.\n   */\n  template <typename Number, int n_comp, int simd_length, typename FullMatrix>\n  void distribute_local_to_global(\n      const FullMatrix &cell_matrix,\n      const std::vector<dealii::types::global_dof_index> &dof_indices_row,\n      const std::vector<dealii::types::global_dof_index> &dof_indices_column,\n      const dealii::AffineConstraints<Number> &affine_constraints,\n      SparseMatrix<Number, n_comp, simd_length> &sparse_matrix);\n\n\n  /*\n   * Variant of the function above that takes a symmetric matrix\n   * constribution where the column and row indices are the same.\n   */\n  template <typename Number,\n            int n_comp,\n            int simd_length,\n            typename FullMatrix,\n            typename Vector>\n  void distribute_local_to_global(\n      const FullMatrix &cell_matrix,\n      const std::vector<dealii::types::global_dof_index> &dof_indices,\n      const dealii::AffineConstraints<Number> &affine_constraints,\n      SparseMatrix<Number, n_comp, simd_length> &sparse_matrix);\n\n\n#ifndef DOXYGEN\n  /*\n   * -------------------------------------------------------------------------\n   * Inline function definitions\n   * -------------------------------------------------------------------------\n   */\n\n\n  template <typename Number, int n_components, int simd_length>\n  SparseMatrix<Number, n_components, simd_length>::SparseMatrix(\n      const SparsityPattern<simd_length> &sparsity)\n  {\n    reinit(sparsity);\n  }\n\n\n  template <typename Number, int n_components, int simd_length>\n  void SparseMatrix<Number, n_components, simd_length>::reinit(\n      const SparsityPattern<simd_length> &sparsity)\n  {\n    this->sparsity_pattern_ = &sparsity;\n    this->host_space_active_ = true;\n\n    using KokkosHost = dealii::MemorySpace::Host::kokkos_space;\n    using KokkosDefault = dealii::MemorySpace::Default::kokkos_space;\n    using Aligned = Kokkos::MemoryTraits<Kokkos::Aligned>;\n\n    data_host_ = Kokkos::View<Number *, KokkosHost, Aligned>(\n        \"sparse_matrix_data\", sparsity.n_nonzero_elements() * n_components);\n\n    data_default_ = Kokkos::create_mirror_view(\n        typename KokkosDefault::execution_space(), data_host_);\n\n    const std::size_t n_indices = sparsity.entries_to_be_sent().size();\n\n    exchange_buffer_host_ = Kokkos::View<Number *, KokkosHost, Aligned>(\n        \"sparse_matrix_exchange_buffer\", n_components * n_indices);\n\n    exchange_buffer_default_ = Kokkos::create_mirror_view(\n        typename KokkosDefault::execution_space(), exchange_buffer_host_);\n\n    /* reinitialize the view: */\n    SparseMatrixView<Number, n_components, simd_length>::reinit(*this);\n  }\n\n\n  template <typename Number, int n_comp, int simd_length>\n  template <typename MemorySpace>\n  SparseMatrixView<Number, n_comp, simd_length, MemorySpace, true>\n  SparseMatrix<Number, n_comp, simd_length>::get_view()\n  {\n    Assert(is_active_memory_space<MemorySpace>(),\n           dealii::ExcMessage(\"The chosen memory space is not active.\"));\n\n    return SparseMatrixView<Number, n_comp, simd_length, MemorySpace, true>(\n        *this);\n  }\n\n\n  template <typename Number, int n_comp, int simd_length>\n  template <typename MemorySpace>\n  SparseMatrixView<Number, n_comp, simd_length, MemorySpace, false>\n  SparseMatrix<Number, n_comp, simd_length>::get_view() const\n  {\n    Assert(is_active_memory_space<MemorySpace>(),\n           dealii::ExcMessage(\"The chosen memory space is not active.\"));\n\n    return SparseMatrixView<Number, n_comp, simd_length, MemorySpace, false>(\n        *this);\n  }\n\n\n  template <typename Number, int n_components, int simd_length>\n  template <typename MemorySpace>\n  bool SparseMatrix<Number, n_components, simd_length>::is_active_memory_space()\n      const\n  {\n    using HostSpace = dealii::MemorySpace::Host;\n    using DefaultSpace = dealii::MemorySpace::Default;\n    static_assert(std::is_same_v<MemorySpace, HostSpace> ||\n                      std::is_same_v<MemorySpace, DefaultSpace>,\n                  \"Unexpected memory space\");\n\n    return host_space_active_ == std::is_same_v<MemorySpace, HostSpace>;\n  }\n\n\n  template <typename Number, int n_components, int simd_length>\n  template <typename MemorySpace>\n  void SparseMatrix<Number, n_components, simd_length>::move_to_memory_space()\n  {\n    using HostSpace = dealii::MemorySpace::Host;\n    using DefaultSpace = dealii::MemorySpace::Default;\n    static_assert(std::is_same_v<MemorySpace, HostSpace> ||\n                      std::is_same_v<MemorySpace, DefaultSpace>,\n                  \"Unexpected memory space\");\n\n    if (is_active_memory_space<MemorySpace>())\n      return;\n\n    /* No copy required if default and host are the same memory spaces: */\n    constexpr bool move_required =\n        !std::is_same_v<HostSpace::kokkos_space, DefaultSpace::kokkos_space>;\n\n    if constexpr (std::is_same_v<MemorySpace, HostSpace>) {\n      host_space_active_ = true;\n      if constexpr (move_required) {\n        Kokkos::deep_copy(/*dst*/ data_host_, /*src*/ data_default_);\n        Kokkos::deep_copy(/*dst*/ exchange_buffer_host_,\n                          /*src*/ exchange_buffer_default_);\n      }\n    } else if constexpr (std::is_same_v<MemorySpace, DefaultSpace>) {\n      host_space_active_ = false;\n      if constexpr (move_required) {\n        Kokkos::deep_copy(/*dst*/ data_default_, /*src*/ data_host_);\n        Kokkos::deep_copy(/*dst*/ exchange_buffer_default_,\n                          /*src*/ exchange_buffer_host_);\n      }\n    }\n  }\n\n\n  template <typename Number, int n_components, int simd_length>\n  template <typename MemorySpace>\n  void SparseMatrix<Number, n_components, simd_length>::\n      zero_out_ghost_rows_on_memory_space()\n  {\n    using HostSpace = dealii::MemorySpace::Host;\n    using DefaultSpace = dealii::MemorySpace::Default;\n\n    Assert(is_active_memory_space<MemorySpace>(),\n           dealii::ExcMessage(\"The chosen memory space is not active.\"));\n\n    AssertThrow((std::is_same_v<MemorySpace, HostSpace>),\n                dealii::ExcNotImplemented());\n\n    const auto ghost_offset =\n        sparsity_pattern_->template ghost_offset<n_components>();\n    const auto end_offset =\n        sparsity_pattern_->n_nonzero_elements() * n_components;\n    std::fill(data_host_.data() + ghost_offset,\n              data_host_.data() + end_offset,\n              Number{});\n  }\n\n\n  template <typename Number, int n_components, int simd_length>\n  template <typename MemorySpace>\n  void SparseMatrix<Number, n_components, simd_length>::\n      update_ghost_rows_on_memory_space()\n  {\n    using HostSpace = dealii::MemorySpace::Host;\n    using DefaultSpace = dealii::MemorySpace::Default;\n\n    AssertThrow((std::is_same_v<MemorySpace, HostSpace>),\n                dealii::ExcNotImplemented());\n\n    Assert(is_active_memory_space<MemorySpace>(),\n           dealii::ExcMessage(\"The chosen memory space is not active.\"));\n\n    const auto &receive_targets = sparsity_pattern_->receive_targets();\n    const auto &send_targets = sparsity_pattern_->send_targets();\n    const auto &entries_to_be_sent = sparsity_pattern_->entries_to_be_sent();\n\n    const unsigned int mpi_tag =\n        dealii::Utilities::MPI::internal::Tags::partitioner_export_start + 0;\n    Assert(mpi_tag <=\n               dealii::Utilities::MPI::internal::Tags::partitioner_export_end,\n           dealii::ExcInternalError());\n\n    const unsigned int n_requests =\n        receive_targets.size() + send_targets.size();\n    std::vector<MPI_Request> requests(n_requests);\n\n    const auto ghost_offset =\n        sparsity_pattern_->template ghost_offset<n_components>();\n\n    for (unsigned int p = 0; p < receive_targets.size(); ++p) {\n      const auto receive_offset =\n          n_components * (p == 0 ? 0 : receive_targets[p - 1].second);\n      const auto receive_size =\n          (receive_targets[p].second * n_components - receive_offset);\n\n#ifdef DEBUG_MPI_EXCHANGE\n      const auto mpi_rank =\n          dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD);\n      std::cout << \"Rank \" << mpi_rank << \" receive from \"\n                << receive_targets[p].first << \" offset = \" << receive_offset\n                << \" size = \" << receive_size << std::endl;\n#endif\n\n      const int ierr =\n          MPI_Irecv(data_host_.data() + ghost_offset + receive_offset,\n                    receive_size,\n                    dealii::Utilities::MPI::mpi_type_id_for_type<Number>,\n                    receive_targets[p].first,\n                    mpi_tag,\n                    sparsity_pattern_->partitioner()->get_mpi_communicator(),\n                    &requests[p]);\n      AssertThrowMPI(ierr);\n    }\n\n    for (std::size_t c = 0; c < entries_to_be_sent.size(); ++c) {\n      const auto &[row, column_index] = entries_to_be_sent[c];\n      for (unsigned int d = 0; d < n_components; ++d) {\n        const auto offset = sparsity_pattern_->template offset<n_components>(\n            row, column_index, d);\n        exchange_buffer_host_(n_components * c + d) = data_host_(offset);\n      }\n    }\n\n    for (unsigned int p = 0; p < send_targets.size(); ++p) {\n      const auto send_offset =\n          n_components * (p == 0 ? 0 : send_targets[p - 1].second);\n      const auto send_size =\n          (send_targets[p].second * n_components - send_offset);\n\n#ifdef DEBUG_MPI_EXCHANGE\n      const auto mpi_rank =\n          dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD);\n      std::cout << \"Rank \" << mpi_rank << \" send to \" << send_targets[p].first\n                << \" offset = \" << send_offset << \" size = \" << send_size\n                << std::endl;\n#endif\n\n      const int ierr =\n          MPI_Isend(exchange_buffer_host_.data() + send_offset,\n                    send_size,\n                    dealii::Utilities::MPI::mpi_type_id_for_type<Number>,\n                    send_targets[p].first,\n                    mpi_tag,\n                    sparsity_pattern_->partitioner()->get_mpi_communicator(),\n                    &requests[receive_targets.size() + p]);\n      AssertThrowMPI(ierr);\n    }\n\n#ifdef DEBUG_MPI_EXCHANGE\n    using namespace std::chrono_literals;\n    std::this_thread::sleep_for(200ms);\n#endif\n\n    const int ierr =\n        MPI_Waitall(requests.size(), requests.data(), MPI_STATUSES_IGNORE);\n    AssertThrowMPI(ierr);\n  }\n\n\n  template <typename Number, int n_components, int simd_length>\n  template <typename MemorySpace>\n  void\n  SparseMatrix<Number, n_components, simd_length>::compress_on_memory_space(\n      dealii::VectorOperation::values operation [[maybe_unused]])\n  {\n    AssertThrow(operation == dealii::VectorOperation::add,\n                dealii::ExcNotImplemented());\n\n    using HostSpace = dealii::MemorySpace::Host;\n    using DefaultSpace = dealii::MemorySpace::Default;\n\n    AssertThrow((std::is_same_v<MemorySpace, HostSpace>),\n                dealii::ExcNotImplemented());\n\n    Assert(is_active_memory_space<MemorySpace>(),\n           dealii::ExcMessage(\"The chosen memory space is not active.\"));\n\n    const auto &receive_targets = sparsity_pattern_->receive_targets();\n    const auto &send_targets = sparsity_pattern_->send_targets();\n    const auto &entries_to_be_sent = sparsity_pattern_->entries_to_be_sent();\n\n    const unsigned int mpi_tag =\n        dealii::Utilities::MPI::internal::Tags::partitioner_export_start + 0;\n    Assert(mpi_tag <=\n               dealii::Utilities::MPI::internal::Tags::partitioner_export_end,\n           dealii::ExcInternalError());\n\n    const unsigned int n_requests =\n        receive_targets.size() + send_targets.size();\n    std::vector<MPI_Request> requests(n_requests);\n\n    /*\n     * Note: For the compress() operation we receive from the \"send\n     * targets\" and store in the exchange buffer.\n     */\n\n    for (unsigned int p = 0; p < send_targets.size(); ++p) {\n      const auto receive_offset =\n          n_components * (p == 0 ? 0 : send_targets[p - 1].second);\n      const auto receive_size =\n          (send_targets[p].second * n_components - receive_offset);\n\n#ifdef DEBUG_MPI_EXCHANGE\n      const auto mpi_rank =\n          dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD);\n      std::cout << \"Rank \" << mpi_rank << \" receive from \"\n                << send_targets[p].first << \" offset = \" << receive_offset\n                << \" size = \" << receive_size << std::endl;\n#endif\n\n      const int ierr =\n          MPI_Irecv(exchange_buffer_host_.data() + receive_offset,\n                    receive_size,\n                    dealii::Utilities::MPI::mpi_type_id_for_type<Number>,\n                    send_targets[p].first,\n                    mpi_tag,\n                    sparsity_pattern_->partitioner()->get_mpi_communicator(),\n                    &requests[p]);\n      AssertThrowMPI(ierr);\n    }\n\n    const auto ghost_offset =\n        sparsity_pattern_->template ghost_offset<n_components>();\n\n    /*\n     * Note: For the compress() operation we send our ghost range to the\n     * \"receive targets\".\n     */\n\n    for (unsigned int p = 0; p < receive_targets.size(); ++p) {\n      const auto send_offset =\n          n_components * (p == 0 ? 0 : receive_targets[p - 1].second);\n      const auto send_size =\n          (receive_targets[p].second * n_components - send_offset);\n\n#ifdef DEBUG_MPI_EXCHANGE\n      const auto mpi_rank =\n          dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD);\n      std::cout << \"Rank \" << mpi_rank << \" send to \"\n                << receive_targets[p].first << \" offset = \" << send_offset\n                << \" size = \" << send_size << std::endl;\n#endif\n\n      const int ierr =\n          MPI_Isend(data_host_.data() + ghost_offset + send_offset,\n                    send_size,\n                    dealii::Utilities::MPI::mpi_type_id_for_type<Number>,\n                    receive_targets[p].first,\n                    mpi_tag,\n                    sparsity_pattern_->partitioner()->get_mpi_communicator(),\n                    &requests[send_targets.size() + p]);\n      AssertThrowMPI(ierr);\n    }\n\n#ifdef DEBUG_MPI_EXCHANGE\n    using namespace std::chrono_literals;\n    std::this_thread::sleep_for(200ms);\n#endif\n\n    const int ierr =\n        MPI_Waitall(requests.size(), requests.data(), MPI_STATUSES_IGNORE);\n    AssertThrowMPI(ierr);\n\n    /* Add back contributions and clear ghost range: */\n\n    for (std::size_t c = 0; c < entries_to_be_sent.size(); ++c) {\n      const auto &[row, column_index] = entries_to_be_sent[c];\n      for (unsigned int d = 0; d < n_components; ++d) {\n        const auto offset = sparsity_pattern_->template offset<n_components>(\n            row, column_index, d);\n        data_host_(offset) += exchange_buffer_host_(n_components * c + d);\n      }\n    }\n\n    zero_out_ghost_rows_on_memory_space<MemorySpace>();\n  }\n\n\n  template <typename Number,\n            int n_comp,\n            int simd_length,\n            typename MemorySpace,\n            bool writable>\n  SparseMatrixView<Number, n_comp, simd_length, MemorySpace, writable>::\n      SparseMatrixView(SparseMatrix<Number, n_comp, simd_length> &sparse_matrix)\n    requires(writable)\n  {\n    reinit(sparse_matrix);\n  }\n\n\n  template <typename Number,\n            int n_comp,\n            int simd_length,\n            typename MemorySpace,\n            bool writable>\n  SparseMatrixView<Number, n_comp, simd_length, MemorySpace, writable>::\n      SparseMatrixView(\n          const SparseMatrix<Number, n_comp, simd_length> &sparse_matrix)\n    requires(!writable)\n  {\n    reinit(sparse_matrix);\n  }\n\n\n  template <typename Number,\n            int n_comp,\n            int simd_length,\n            typename MemorySpace,\n            bool writable>\n  template <typename SparseMatrix>\n  void\n  SparseMatrixView<Number, n_comp, simd_length, MemorySpace, writable>::reinit(\n      SparseMatrix &sparse_matrix)\n    requires(writable != std::is_const_v<SparseMatrix>)\n  {\n    using HostSpace = dealii::MemorySpace::Host;\n    using DefaultSpace = dealii::MemorySpace::Default;\n\n    static_assert(std::is_same_v<MemorySpace, HostSpace> ||\n                      std::is_same_v<MemorySpace, DefaultSpace>,\n                  \"Unexpected Kokkos memory space\");\n\n    sparse_matrix_ = &sparse_matrix;\n\n    if constexpr (std::is_same_v<MemorySpace, HostSpace>) {\n      data_ = sparse_matrix.data_host_;\n    } else {\n      data_ = sparse_matrix.data_default_;\n    }\n\n    sparsity_pattern_ =\n        sparse_matrix.sparsity_pattern_->template get_view<MemorySpace>();\n  }\n\n\n  template <typename Number,\n            int n_comp,\n            int simd_length,\n            typename MemorySpace,\n            bool writable>\n  template <typename Number2>\n  DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number2\n  SparseMatrixView<Number, n_comp, simd_length, MemorySpace, writable>::\n      read_entry(const unsigned int row, const unsigned int column_index) const\n  {\n    static_assert(\n        n_comp == 1,\n        \"Attempted to write a scalar value into a tensor-valued matrix entry\");\n\n    const auto result = read_tensor<Number2>(row, column_index);\n    return result[0];\n  }\n\n\n  template <typename Number,\n            int n_comp,\n            int simd_length,\n            typename MemorySpace,\n            bool writable>\n  template <typename Number2, typename Tensor>\n  DEAL_II_HOST_DEVICE_ALWAYS_INLINE Tensor\n  SparseMatrixView<Number, n_comp, simd_length, MemorySpace, writable>::\n      read_tensor(const unsigned int row, const unsigned int column_index) const\n  {\n    static_assert(std::is_same_v<Number2, typename Tensor::value_type>,\n                  \"type mismatch\");\n\n    AssertIndexRange(row, sparsity_pattern_.n_rows());\n    AssertIndexRange(column_index, sparsity_pattern_.row_length(row));\n\n    Tensor result;\n\n    using VA = dealii::VectorizedArray<Number>;\n    if constexpr (std::is_same_v<VA, Number2>) {\n      /*\n       * Vectorized fast access. Indices must be in the range\n       * [0,n_internal), index must be divisible by simd_length\n       */\n\n      Assert(row < sparsity_pattern_.n_internal_dofs(),\n             dealii::ExcMessage(\n                 \"Vectorized access only possible in vectorized part\"));\n      Assert(row % simd_length == 0,\n             dealii::ExcMessage(\n                 \"Access only supported for rows at the SIMD granularity\"));\n\n      const Number *load_pos = data_.data();\n      load_pos +=\n          sparsity_pattern_.template offset_internal<n_comp>(row, column_index);\n\n      for (unsigned int d = 0; d < n_comp; ++d)\n        result[d].load(load_pos + d * simd_length);\n\n    } else {\n      /*\n       * Non-vectorized slow access. Supports all row indices in [0,n_owned):\n       */\n\n      for (unsigned int d = 0; d < n_comp; ++d) {\n        const auto offset =\n            sparsity_pattern_.template offset<n_comp>(row, column_index, d);\n        result[d] = data_(offset);\n      }\n    }\n\n    return result;\n  }\n\n\n  template <typename Number,\n            int n_comp,\n            int simd_length,\n            typename MemorySpace,\n            bool writable>\n  template <typename Number2>\n  DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number2\n  SparseMatrixView<Number, n_comp, simd_length, MemorySpace, writable>::\n      read_transposed_entry(const unsigned int row,\n                            const unsigned int column_index) const\n  {\n    static_assert(\n        n_comp == 1,\n        \"Attempted to write a scalar value into a tensor-valued matrix entry\");\n\n    const auto result = read_transposed_tensor<Number2>(row, column_index);\n    return result[0];\n  }\n\n\n  template <typename Number,\n            int n_comp,\n            int simd_length,\n            typename MemorySpace,\n            bool writable>\n  template <typename Number2, typename Tensor>\n  DEAL_II_HOST_DEVICE_ALWAYS_INLINE Tensor\n  SparseMatrixView<Number, n_comp, simd_length, MemorySpace, writable>::\n      read_transposed_tensor(const unsigned int row,\n                             const unsigned int column_index) const\n  {\n    static_assert(std::is_same_v<Number2, typename Tensor::value_type>,\n                  \"type mismatch\");\n\n    AssertIndexRange(row, sparsity_pattern_.n_rows());\n    AssertIndexRange(column_index, sparsity_pattern_.row_length(row));\n\n    dealii::Tensor<1, n_comp, Number2> result;\n\n    using VA = dealii::VectorizedArray<Number>;\n    if constexpr (std::is_same_v<VA, Number2> && (n_comp == 1)) {\n      /*\n       * Vectorized fast access. Indices must be in the range\n       * [0,n_internal), index must be divisible by simd_length\n       */\n\n      Assert(row < sparsity_pattern_.n_internal_dofs(),\n             dealii::ExcMessage(\n                 \"Vectorized access only possible in vectorized part\"));\n      Assert(row % simd_length == 0,\n             dealii::ExcMessage(\n                 \"Access only supported for rows at the SIMD granularity\"));\n\n      const auto offsets =\n          sparsity_pattern_.template transposed_offset_internal<1>(\n              row, column_index);\n      result[0].gather(data_.data(), offsets);\n\n    } else if constexpr (std::is_same_v<VA, Number2> && (n_comp != 1)) {\n\n      /* not implemented */\n      Assert(false,\n             dealii::ExcMessage(\"Vectorized transposed access to multiple \"\n                                \"components is not implemented.\"));\n      __builtin_trap();\n\n    } else {\n      /*\n       * Non-vectorized slow access. Supports all row indices in [0,n_owned):\n       */\n\n      for (unsigned int d = 0; d < n_comp; ++d) {\n        const auto offset =\n            sparsity_pattern_.template transposed_offset<n_comp>(\n                row, column_index, d);\n        result[d] = data_(offset);\n      }\n    }\n\n    return result;\n  }\n\n\n  template <typename Number,\n            int n_comp,\n            int simd_length,\n            typename MemorySpace,\n            bool writable>\n  template <typename Number2>\n  DEAL_II_HOST_DEVICE_ALWAYS_INLINE void\n  SparseMatrixView<Number, n_comp, simd_length, MemorySpace, writable>::\n      write_entry(const Number2 entry,\n                  const unsigned int row,\n                  const unsigned int column_index,\n                  const bool do_streaming_store) const\n    requires(writable)\n  {\n    static_assert(\n        n_comp == 1,\n        \"Attempted to write a scalar value into a tensor-valued matrix entry\");\n\n    AssertIndexRange(row, sparsity_pattern_.n_rows());\n    AssertIndexRange(column_index, sparsity_pattern_.row_length(row));\n\n    dealii::Tensor<1, n_comp, Number2> tensor;\n    tensor[0] = entry;\n\n    write_tensor<Number2>(tensor, row, column_index, do_streaming_store);\n  }\n\n\n  template <typename Number,\n            int n_comp,\n            int simd_length,\n            typename MemorySpace,\n            bool writable>\n  template <typename Number2, typename Tensor>\n  DEAL_II_HOST_DEVICE_ALWAYS_INLINE void\n  SparseMatrixView<Number, n_comp, simd_length, MemorySpace, writable>::\n      write_tensor(const Tensor &tensor,\n                   const unsigned int row,\n                   const unsigned int column_index,\n                   const bool do_streaming_store) const\n    requires(writable)\n  {\n    AssertIndexRange(row, sparsity_pattern_.n_rows());\n    AssertIndexRange(column_index, sparsity_pattern_.row_length(row));\n\n    using VA = dealii::VectorizedArray<Number>;\n    if constexpr (std::is_same_v<VA, Number2>) {\n      /*\n       * Vectorized fast access. Indices must be in the range [0,n_internal),\n       * index must be divisible by simd_length:\n       */\n\n      Assert(row < sparsity_pattern_.n_internal_dofs(),\n             dealii::ExcMessage(\n                 \"Vectorized access only possible in vectorized part\"));\n      Assert(row % simd_length == 0,\n             dealii::ExcMessage(\n                 \"Access only supported for rows at the SIMD granularity\"));\n\n      Number *store_pos = data_.data();\n      store_pos +=\n          sparsity_pattern_.template offset_internal<n_comp>(row, column_index);\n\n      if (do_streaming_store)\n        for (unsigned int d = 0; d < n_comp; ++d)\n          tensor[d].streaming_store(store_pos + d * simd_length);\n      else\n        for (unsigned int d = 0; d < n_comp; ++d)\n          tensor[d].store(store_pos + d * simd_length);\n\n    } else {\n      /*\n       * Non-vectorized slow access. Supports all row indices in [0,n_owned):\n       */\n\n      for (unsigned int d = 0; d < n_comp; ++d) {\n        const auto offset =\n            sparsity_pattern_.template offset<n_comp>(row, column_index, d);\n        data_(offset) = tensor[d];\n      }\n    }\n  }\n\n\n  template <typename Number,\n            int n_comp,\n            int simd_length,\n            typename MemorySpace,\n            bool writable>\n  template <typename Number2>\n  DEAL_II_HOST_DEVICE_ALWAYS_INLINE void\n  SparseMatrixView<Number, n_comp, simd_length, MemorySpace, writable>::\n      add_entry(const Number2 entry,\n                const unsigned int row,\n                const unsigned int column_index) const\n    requires(writable)\n  {\n    static_assert(\n        n_comp == 1,\n        \"Attempted to write a scalar value into a tensor-valued matrix entry\");\n\n    AssertIndexRange(row, sparsity_pattern_.n_rows());\n    AssertIndexRange(column_index, sparsity_pattern_.row_length(row));\n\n    dealii::Tensor<1, n_comp, Number2> tensor;\n    tensor[0] = entry;\n\n    add_tensor<Number2>(tensor, row, column_index);\n  }\n\n\n  template <typename Number,\n            int n_comp,\n            int simd_length,\n            typename MemorySpace,\n            bool writable>\n  template <typename Number2, typename Tensor>\n  DEAL_II_HOST_DEVICE_ALWAYS_INLINE void\n  SparseMatrixView<Number, n_comp, simd_length, MemorySpace, writable>::\n      add_tensor(const Tensor &tensor,\n                 const unsigned int row,\n                 const unsigned int column_index) const\n    requires(writable)\n  {\n    AssertIndexRange(row, sparsity_pattern_.n_rows());\n    AssertIndexRange(column_index, sparsity_pattern_.row_length(row));\n\n    using VA = dealii::VectorizedArray<Number>;\n    if constexpr (std::is_same_v<VA, Number2>) {\n      /*\n       * Vectorized fast access. Indices must be in the range [0,n_internal),\n       * index must be divisible by simd_length:\n       */\n\n      Assert(row < sparsity_pattern_.n_internal_dofs(),\n             dealii::ExcMessage(\n                 \"Vectorized access only possible in vectorized part\"));\n      Assert(row % simd_length == 0,\n             dealii::ExcMessage(\n                 \"Access only supported for rows at the SIMD granularity\"));\n\n      Number *store_pos = data_.data();\n      store_pos +=\n          sparsity_pattern_.template offset_internal<n_comp>(row, column_index);\n\n      for (unsigned int d = 0; d < n_comp; ++d) {\n        auto temp = tensor[d];\n        temp.load(store_pos + d * simd_length);\n        temp += tensor[d];\n        temp.store(store_pos + d * simd_length);\n      }\n\n    } else {\n      /*\n       * Non-vectorized slow access. Supports all row indices in [0,n_owned):\n       */\n\n      for (unsigned int d = 0; d < n_comp; ++d) {\n        const auto offset =\n            sparsity_pattern_.template offset<n_comp>(row, column_index, d);\n        data_(offset) += tensor[d]; /*add*/\n        ;\n      }\n    }\n  }\n\n\n  template <typename Number,\n            int n_comp,\n            int simd_length,\n            typename MemorySpace,\n            bool writable>\n  void SparseMatrixView<Number, n_comp, simd_length, MemorySpace, writable>::\n      zero_out_ghost_rows() const\n    requires(writable)\n  {\n    using HostSpace = dealii::MemorySpace::Host;\n    using DefaultSpace = dealii::MemorySpace::Default;\n\n    static_assert(std::is_same_v<MemorySpace, HostSpace> ||\n                      std::is_same_v<MemorySpace, DefaultSpace>,\n                  \"Unexpected Kokkos memory space\");\n\n    Assert(sparse_matrix_->template is_active_memory_space<MemorySpace>(),\n           dealii::ExcMessage(\"The chosen memory space is not active.\"));\n\n    sparse_matrix_->template zero_out_ghost_rows<MemorySpace>();\n  }\n\n\n  template <typename Number,\n            int n_comp,\n            int simd_length,\n            typename MemorySpace,\n            bool writable>\n  void SparseMatrixView<Number, n_comp, simd_length, MemorySpace, writable>::\n      update_ghost_rows() const\n    requires(writable)\n  {\n    using HostSpace = dealii::MemorySpace::Host;\n    using DefaultSpace = dealii::MemorySpace::Default;\n\n    static_assert(std::is_same_v<MemorySpace, HostSpace> ||\n                      std::is_same_v<MemorySpace, DefaultSpace>,\n                  \"Unexpected Kokkos memory space\");\n\n    Assert(sparse_matrix_->template is_active_memory_space<MemorySpace>(),\n           dealii::ExcMessage(\"The chosen memory space is not active.\"));\n\n    sparse_matrix_->template update_ghost_rows_on_memory_space<MemorySpace>();\n  }\n\n\n  template <typename Number,\n            int n_comp,\n            int simd_length,\n            typename MemorySpace,\n            bool writable>\n  void SparseMatrixView<Number, n_comp, simd_length, MemorySpace, writable>::\n      compress(dealii::VectorOperation::values operation) const\n    requires(writable)\n  {\n    using HostSpace = dealii::MemorySpace::Host;\n    using DefaultSpace = dealii::MemorySpace::Default;\n\n    static_assert(std::is_same_v<MemorySpace, HostSpace> ||\n                      std::is_same_v<MemorySpace, DefaultSpace>,\n                  \"Unexpected Kokkos memory space\");\n\n    Assert(sparse_matrix_->template is_active_memory_space<MemorySpace>(),\n           dealii::ExcMessage(\"The chosen memory space is not active.\"));\n\n    sparse_matrix_->template compress_on_memory_space<MemorySpace>(operation);\n  }\n\n\n  template <typename Number, int n_comp, int simd_length, typename FM>\n  void distribute_local_to_global(\n      const FM &cell_matrix,\n      const std::vector<dealii::types::global_dof_index> &dof_indices_row,\n      const std::vector<dealii::types::global_dof_index> &dof_indices_column,\n      const dealii::AffineConstraints<Number> &affine_constraints\n      [[maybe_unused]],\n      SparseMatrix<Number, n_comp, simd_length> &sparse_matrix)\n  {\n    constexpr bool is_matrix = std::is_same_v<FM, dealii::FullMatrix<Number>>;\n    constexpr bool is_array =\n        std::is_same_v<FM, std::array<dealii::FullMatrix<Number>, n_comp>>;\n    static_assert((n_comp == 1 && is_matrix) || is_array, \"not implemented\");\n\n    if constexpr (is_matrix) {\n      Assert(cell_matrix.m() == dof_indices_row.size(),\n             dealii::ExcInternalError());\n      Assert(cell_matrix.n() == dof_indices_column.size(),\n             dealii::ExcInternalError());\n    } else if constexpr (is_array) {\n      Assert(cell_matrix.size() == n_comp, dealii::ExcInternalError());\n      for (unsigned int d = 0; d < n_comp; ++d) {\n        Assert(cell_matrix[d].m() == dof_indices_row.size(),\n               dealii::ExcInternalError());\n        Assert(cell_matrix[d].n() == dof_indices_column.size(),\n               dealii::ExcInternalError());\n      }\n    }\n\n    const auto &sparsity_pattern = sparse_matrix.sparsity_pattern();\n    const auto &partitioner = sparsity_pattern.partitioner();\n\n    /*\n     * Helper that inserts a single entry into the matrix indexed by (r, c)\n     * in the cell_matrix and by (i, j) in the sparse matrix and multiplied\n     * by a weight c_ij.\n     */\n\n    const auto insert_entry =\n        [&](auto r, auto c, auto i, auto j, auto c_ij) DEAL_II_ALWAYS_INLINE {\n          if constexpr (is_matrix) {\n            const Number &entry = cell_matrix(r, c);\n            if (entry == Number{})\n              return;\n            const auto col_idx = sparsity_pattern.column_index(i, j);\n            sparse_matrix.add_entry(c_ij * entry, i, col_idx);\n          } else if constexpr (is_array) {\n            dealii::Tensor<1, n_comp, Number> entry;\n            for (unsigned int k = 0; k < n_comp; ++k)\n              entry[k] = cell_matrix[k](r, c);\n            if (entry == dealii::Tensor<1, n_comp>{})\n              return;\n            const auto col_idx = sparsity_pattern.column_index(i, j);\n            sparse_matrix.add_tensor(c_ij * entry, i, col_idx);\n          }\n        };\n\n    /*\n     * Helper that iterates over row entries:\n     */\n\n    const auto iterate_over_row_entries =\n        [&](const auto r, const auto i, const auto c_i) DEAL_II_ALWAYS_INLINE {\n          /* Iterate over columns: c - column index; j_global, j - dof index */\n          for (unsigned int c = 0; c < dof_indices_column.size(); ++c) {\n            const auto j_global = dof_indices_column[c];\n            if (affine_constraints.is_constrained(j_global)) {\n              const auto &line =\n                  *affine_constraints.get_constraint_entries(j_global);\n              for (const auto &[k_global, c_k] : line) {\n                const auto k = partitioner->global_to_local(k_global);\n                insert_entry(r, c, i, k, c_i * c_k);\n              }\n            } else {\n              const auto j = partitioner->global_to_local(j_global);\n              insert_entry(r, c, i, j, c_i);\n            }\n          }\n        };\n\n    /* Now, iterate over rows: r - row index; i_global, i - dof index */\n    for (unsigned int r = 0; r < dof_indices_row.size(); ++r) {\n      const auto i_global = dof_indices_row[r];\n      if (affine_constraints.is_constrained(i_global)) {\n        const auto &line = *affine_constraints.get_constraint_entries(i_global);\n        for (const auto &[k_global, c_k] : line) {\n          const auto k = partitioner->global_to_local(k_global);\n          iterate_over_row_entries(r, k, c_k);\n        }\n      } else {\n        const auto i = partitioner->global_to_local(i_global);\n        iterate_over_row_entries(r, i, Number(1.));\n      }\n    }\n  }\n\n\n  template <typename Number, int n_comp, int simd_length, typename FM>\n  void distribute_local_to_global(\n      const FM &cell_matrix,\n      const std::vector<dealii::types::global_dof_index> &dof_indices,\n      const dealii::AffineConstraints<Number> &affine_constraints,\n      SparseMatrix<Number, n_comp, simd_length> &sparse_matrix)\n  {\n    constexpr bool is_matrix = std::is_same_v<FM, dealii::FullMatrix<Number>>;\n    constexpr bool is_array =\n        std::is_same_v<FM, std::array<dealii::FullMatrix<Number>, n_comp>>;\n    static_assert((n_comp == 1 && is_matrix) || is_array, \"not implemented\");\n\n    distribute_local_to_global(cell_matrix,\n                               dof_indices,\n                               dof_indices,\n                               affine_constraints,\n                               sparse_matrix);\n  }\n\n#endif\n} // namespace ryujin\n"
  },
  {
    "path": "source/sparsity_pattern.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2023 by the ryujin authors\n//\n\n#include <compile_time_options.h>\n\n#include \"sparsity_pattern.template.h\"\n\nnamespace ryujin\n{\n  /* instantiations */\n\n  template class SparsityPattern<dealii::VectorizedArray<NUMBER>::size()>;\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/sparsity_pattern.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n#include <convenience_macros.h>\n\n#include <deal.II/base/aligned_vector.h>\n#include <deal.II/base/config.h>\n#include <deal.II/base/partitioner.h>\n#include <deal.II/lac/dynamic_sparsity_pattern.h>\n\nnamespace ryujin\n{\n  template <int simd_length, typename MemorySpace = dealii::MemorySpace::Host>\n  class SparsityPatternView;\n\n\n  /**\n   * A specialized sparsity pattern for efficient, vectorized SIMD access.\n   *\n   * In the vectorized row index region [0, n_internal_dofs) we store data\n   * as an array-of-struct-of-array type as follows:\n   *\n   *  - At the innermost 'array' level, we group data from simd_length rows\n   *    contiguously in memory, using a given column block as determined by\n   *    the sparsity pattern.\n   *\n   *  - Next come the different components in case we have a\n   *    multi-component matrix, i.e., the 'struct' level groups the\n   *    components next to the inner array of row data.\n   *\n   *  - Finally, the outer array aligns the different components in a CSR\n   *    format, i.e., row-by-row (or row-chunk-per-row-chunk) and along\n   *    columns, following the sparsity pattern.\n   *\n   * For the non-vectorized row index region [n_internal_dofs,\n   * n_locally_relevant_dofs) we store the matrix in CSR format (equivalent\n   * to the static dealii::SparsityPattern).\n   */\n  template <int simd_length>\n  class SparsityPattern : public SparsityPatternView<simd_length>\n  {\n  public:\n    /**\n     * @name Constructor, initialization, access.\n     */\n    //@{\n\n    /**\n     * Default constructor.\n     */\n    SparsityPattern();\n\n    /**\n     * Constructor taking a sparsity pattern template, an MPI partitioner\n     * and the number of (regular) internal dofs as an argument. The\n     * constructor calls SparsityPattern::reinit() internally.\n     */\n    SparsityPattern(\n        const unsigned int n_internal_dofs,\n        const dealii::DynamicSparsityPattern &sparsity,\n        const std::shared_ptr<const dealii::Utilities::MPI::Partitioner>\n            &partitioner,\n        bool symmetrize_ghost_range = true);\n\n    /**\n     * Reinit function that reinitializes the SIMD sparsity pattern for a\n     * given sparsity pattern template, an MPI partitioner and the number\n     * of (regular) internal dofs.\n     *\n     * @note If the @p symmetrize_ghost_range parameter is set to true,\n     * then all transpose entries (j, i) are added to the sparsity pattern,\n     * where i is within the locally owned range. This access is required\n     * for our stencil based methods.\n     */\n    void reinit(const unsigned int n_internal_dofs,\n                const dealii::DynamicSparsityPattern &sparsity,\n                const std::shared_ptr<const dealii::Utilities::MPI::Partitioner>\n                    &partitioner,\n                bool symmetrize_ghost_range = true);\n\n    /**\n     * Return a (read only) view on the sparsity pattern for the selected\n     * memory space.\n     */\n    template <typename MemorySpace>\n    SparsityPatternView<simd_length, MemorySpace> get_view() const;\n\n    ACCESSOR_READ_ONLY_NO_DEREFERENCE(partitioner);\n\n    /**\n     * Array listing all (locally owned) entries as a pair {row,\n     * column_index}, potentially duplicated, and arranged\n     * consecutively by send targets.\n     */\n    ACCESSOR_READ_ONLY(entries_to_be_sent);\n\n    /**\n     * All send targets stored as a pair consisting of an MPI rank (first\n     * entry) and a corresponding index range into entries_to_be_sent given\n     * by the half open range [send_targets[p-1].second, send_targets[p])\n     */\n    ACCESSOR_READ_ONLY(send_targets);\n\n    /**\n     * All receive targets are stored as a pair consisting of an MPI rank\n     * (first entry) and a corresponding index range into the (serial)\n     * data array given by the half open range\n     * [receive_targets[p-1].second, receive_targets[p].second).\n     *\n     * Note, that indices into the data array start with the \"locally\n     * relevant\", or \"ghost range\" offset by n_locally_owned_dofs and\n     * multiplied by the number of components stored by the (vector valued)\n     * matrix.\n     */\n    ACCESSOR_READ_ONLY(receive_targets);\n\n  private:\n    //@}\n    /**\n     * @name Internal fields, methods, and friends\n     */\n    //@{\n\n    std::shared_ptr<const dealii::Utilities::MPI::Partitioner> partitioner_;\n\n    unsigned int n_internal_dofs_;\n    unsigned int n_locally_owned_dofs_;\n\n    using KokkosHost = dealii::MemorySpace::Host::kokkos_space;\n    Kokkos::View<unsigned int *, KokkosHost> row_starts_host_;\n    Kokkos::View<unsigned int *, KokkosHost> column_indices_host_;\n    Kokkos::View<unsigned int *, KokkosHost> indices_transposed_host_;\n\n    using KokkosDefault = dealii::MemorySpace::Default::kokkos_space;\n    Kokkos::View<unsigned int *, KokkosDefault> row_starts_default_;\n    Kokkos::View<unsigned int *, KokkosDefault> column_indices_default_;\n    Kokkos::View<unsigned int *, KokkosDefault> indices_transposed_default_;\n\n    std::vector<std::pair<unsigned int, unsigned int>> entries_to_be_sent_;\n    std::vector<std::pair<unsigned int, unsigned int>> send_targets_;\n    std::vector<std::pair<unsigned int, unsigned int>> receive_targets_;\n\n    template <int, typename>\n    friend class SparsityPatternView;\n    //@}\n  };\n\n\n  /**\n   * This class models a \"view\" of the sparsity pattern that lives in the\n   * host or device memory space. It provides a number of methods for\n   * iterating over the sparsity pattern and offset computation.\n   *\n   * @note This class is designed to be captured by value in computation\n   * loops with access to either the host or device memory space. As such\n   * we do not store a reference to the underlying SparsityPattern but\n   * rather raw pointers into the corresponding memory. The view is only\n   * valid as long as the underlying SparsityPattern object is not\n   * modified.\n   */\n  template <int simd_length, typename MemorySpace>\n  class SparsityPatternView\n  {\n  public:\n    /**\n     * @name Constructor, initialization.\n     */\n    //@{\n\n    SparsityPatternView() = default;\n\n    SparsityPatternView(const SparsityPattern<simd_length> &sparsity_pattern);\n\n    void reinit(const SparsityPattern<simd_length> &sparsity_pattern);\n\n    //@}\n    /**\n     * @name General information about the sparsity pattern.\n     */\n    //@{\n\n    /**\n     * The number of locally internal rows of the sparsity pattern that are\n     * stored in optimized \"array-of-struct-of-array\" format.\n     */\n    DEAL_II_HOST_DEVICE\n    unsigned int n_internal_dofs() const;\n\n    /**\n     * The number of locally owned rows of the sparsity pattern.\n     */\n    DEAL_II_HOST_DEVICE\n    unsigned int n_locally_owned_dofs() const;\n\n    /**\n     * The total number of rows of the given sparsity pattern. This number\n     * comprises all locally owned rows and the ghost row range and is\n     * equal to n_locally_relevant.\n     */\n    DEAL_II_HOST_DEVICE\n    unsigned int n_rows() const;\n\n    /**\n     * The total number of nonzero elements of the given sparsity pattern.\n     */\n    DEAL_II_HOST_DEVICE\n    unsigned int n_nonzero_elements() const;\n\n    //@}\n    /**\n     * @name Properties of a row.\n     */\n    //@{\n\n    /**\n     * Return the \"stride size\" of a given row index. The function returns\n     * simd_length for all indices in the range [0, n_internal_dofs) and 1\n     * otherwise.\n     */\n    DEAL_II_HOST_DEVICE\n    unsigned int stride_of_row(const unsigned int row) const;\n\n    /**\n     * Return a pointer to the array of column indices for the given row,\n     * i.e., for a given row index i:\n     * ```\n     * const unsigned int *js = sparsity_pattern.columns(i);\n     * ```\n     * is a pointer to the column index j (or column indices *js when SIMD\n     * vectorized).\n     */\n    DEAL_II_HOST_DEVICE\n    const unsigned int *columns(const unsigned int row) const;\n\n    /**\n     * Return the row length of a given row index.\n     */\n    DEAL_II_HOST_DEVICE\n    unsigned int row_length(const unsigned int row) const;\n\n    /**\n     * Given a row index @p row and a column index @p column return the\n     * corresponding column index, i.e., the position of the column in the\n     * stencil of nonzero row entries.\n     */\n    DEAL_II_HOST_DEVICE\n    unsigned int column_index(const unsigned int row,\n                              const unsigned int column) const;\n\n    //@}\n    /**\n     * @name Offset calculation\n     */\n    //@{\n\n    /**\n     * Given a row index, an index for the column (within [0,\n     * row_length(row)), and a component index return the position of the\n     * matrix entry in the data array.\n     */\n    template <unsigned int n_components = 1>\n    DEAL_II_HOST_DEVICE unsigned int\n    offset(const unsigned int row,\n           const unsigned int column_index,\n           const unsigned int component = 0) const;\n\n    /**\n     * Specialized version of the function above that computes the offset\n     * only for the internal part and pointing to component 0. This variant\n     * avoids a number of index computations and an if statement.\n     *\n     * @pre row must be within the internal index range.\n     */\n    template <unsigned int n_components = 1>\n    DEAL_II_HOST_DEVICE unsigned int\n    offset_internal(const unsigned int row,\n                    const unsigned int column_index) const;\n\n    /**\n     * Given a row index, an index for the column (within [0,\n     * row_length(row)), and a component index return the position of the\n     * *transposed* matrix entry in the data array.\n     */\n    template <unsigned int n_components = 1>\n    DEAL_II_HOST_DEVICE unsigned int\n    transposed_offset(const unsigned int row,\n                      const unsigned int column_index,\n                      const unsigned int component = 0) const;\n\n    /**\n     * Specialized version of the function above that computes the offset\n     * only for the internal part and pointing to component 0. This variant\n     * avoids a number of index computations and an if statement.\n     *\n     * @pre row must be within the internal index range.\n     */\n    template <unsigned int n_components = 1>\n    DEAL_II_HOST_DEVICE const unsigned int *\n    transposed_offset_internal(const unsigned int row,\n                               const unsigned int column_index) const;\n\n    /**\n     * Return an offset pointing to the first element of the ghost range,\n     * i.e., the value corresponding to\n     * `offset(sparsity_->n_locally_owned_dofs(), 0, 0)`.\n     *\n     * @note If the sparsity pattern does not contain a ghost range, then\n     * the offset points one element past the data array of the sparse\n     * matrix.\n     */\n    template <unsigned int n_components = 1>\n    DEAL_II_HOST_DEVICE unsigned int ghost_offset() const;\n\n    //@}\n    /**\n     * @name Internal fields, methods, and friends\n     */\n    //@{\n\n  private:\n    unsigned int n_internal_dofs_;\n    unsigned int n_locally_owned_dofs_;\n\n    using KokkosSpace = typename MemorySpace::kokkos_space;\n    Kokkos::View<const unsigned int *, KokkosSpace> row_starts_;\n    Kokkos::View<const unsigned int *, KokkosSpace> column_indices_;\n    Kokkos::View<const unsigned int *, KokkosSpace> indices_transposed_;\n    //@}\n  };\n\n\n#ifndef DOXYGEN\n  /*\n   * -------------------------------------------------------------------------\n   * Inline function definitions\n   * -------------------------------------------------------------------------\n   */\n\n\n  template <int simd_length>\n  template <typename MemorySpace>\n  SparsityPatternView<simd_length, MemorySpace>\n  SparsityPattern<simd_length>::get_view() const\n  {\n    return SparsityPatternView<simd_length, MemorySpace>(*this);\n  }\n\n\n  template <int simd_length, typename MemorySpace>\n  SparsityPatternView<simd_length, MemorySpace>::SparsityPatternView(\n      const SparsityPattern<simd_length> &sparsity_pattern)\n  {\n    reinit(sparsity_pattern);\n  }\n\n\n  template <int simd_length, typename MemorySpace>\n  void SparsityPatternView<simd_length, MemorySpace>::reinit(\n      const SparsityPattern<simd_length> &sparsity_pattern)\n  {\n    n_internal_dofs_ = sparsity_pattern.n_internal_dofs_;\n    n_locally_owned_dofs_ = sparsity_pattern.n_locally_owned_dofs_;\n\n    using HostSpace = dealii::MemorySpace::Host;\n    using DefaultSpace = dealii::MemorySpace::Default;\n\n    static_assert(std::is_same_v<MemorySpace, HostSpace> ||\n                      std::is_same_v<MemorySpace, DefaultSpace>,\n                  \"Unexpected memory space\");\n\n    if constexpr (std::is_same_v<MemorySpace, HostSpace>) {\n      row_starts_ = sparsity_pattern.row_starts_host_;\n      column_indices_ = sparsity_pattern.column_indices_host_;\n      indices_transposed_ = sparsity_pattern.indices_transposed_host_;\n    } else {\n      row_starts_ = sparsity_pattern.row_starts_default_;\n      column_indices_ = sparsity_pattern.column_indices_default_;\n      indices_transposed_ = sparsity_pattern.indices_transposed_default_;\n    }\n  }\n\n\n  template <int simd_length, typename MemorySpace>\n  DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int\n  SparsityPatternView<simd_length, MemorySpace>::n_internal_dofs() const\n  {\n    return n_internal_dofs_;\n  }\n\n\n  template <int simd_length, typename MemorySpace>\n  DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int\n  SparsityPatternView<simd_length, MemorySpace>::n_locally_owned_dofs() const\n  {\n    return n_locally_owned_dofs_;\n  }\n\n\n  template <int simd_length, typename MemorySpace>\n  DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int\n  SparsityPatternView<simd_length, MemorySpace>::n_rows() const\n  {\n    Assert(row_starts_.size() > 0, dealii::ExcNotInitialized());\n\n    return row_starts_.size() - 1;\n  }\n\n\n  template <int simd_length, typename MemorySpace>\n  DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int\n  SparsityPatternView<simd_length, MemorySpace>::n_nonzero_elements() const\n  {\n    Assert(row_starts_.size() > 0, dealii::ExcNotInitialized());\n\n    return row_starts_(row_starts_.size() - 1);\n  }\n\n\n  template <int simd_length, typename MemorySpace>\n  DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int\n  SparsityPatternView<simd_length, MemorySpace>::stride_of_row(\n      const unsigned int row) const\n  {\n    AssertIndexRange(row, n_rows());\n\n    if (row < n_internal_dofs_)\n      return simd_length;\n    else\n      return 1;\n  }\n\n\n  template <int simd_length, typename MemorySpace>\n  DEAL_II_HOST_DEVICE_ALWAYS_INLINE const unsigned int *\n  SparsityPatternView<simd_length, MemorySpace>::columns(\n      const unsigned int row) const\n  {\n    AssertIndexRange(row, n_rows());\n\n    if (row < n_internal_dofs_)\n      return column_indices_.data() + row_starts_(row / simd_length) +\n             row % simd_length;\n    else\n      return column_indices_.data() + row_starts_(row);\n  }\n\n\n  template <int simd_length, typename MemorySpace>\n  DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int\n  SparsityPatternView<simd_length, MemorySpace>::row_length(\n      const unsigned int row) const\n  {\n    AssertIndexRange(row, n_rows());\n\n    if (row < n_internal_dofs_) {\n      const unsigned int simd_row = row / simd_length;\n      return (row_starts_(simd_row + 1) - row_starts_(simd_row)) / simd_length;\n    } else {\n      return row_starts_(row + 1) - row_starts_(row);\n    }\n  }\n\n\n  template <int simd_length, typename MemorySpace>\n  DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int\n  SparsityPatternView<simd_length, MemorySpace>::column_index(\n      const unsigned int row, const unsigned int column) const\n  {\n    const auto &row_length = this->row_length(row);\n    const auto &stride_size = this->stride_of_row(row);\n\n    // FIXME: with C++23 use std::views::stride and binary search instead...\n\n    const unsigned int *js = columns(row);\n    for (unsigned int k = 0; k < row_length; ++k)\n      if (js[k * stride_size] == column)\n        return k;\n\n    Assert(false, dealii::ExcMessage(\"Column index not found in given row\"));\n    return -1;\n  }\n\n\n  template <int simd_length, typename MemorySpace>\n  template <unsigned int n_components>\n  DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int\n  SparsityPatternView<simd_length, MemorySpace>::offset(\n      const unsigned int row,\n      const unsigned int column_index,\n      const unsigned int comp) const\n  {\n    AssertIndexRange(row, n_rows());\n    AssertIndexRange(column_index, row_length(row));\n    AssertIndexRange(comp, n_components);\n\n    const unsigned int simd_row = row / simd_length;\n    const unsigned int simd_offset = row % simd_length;\n\n    if (row < n_internal_dofs_) {\n      const unsigned int scalar_offset =\n          row_starts_(simd_row) + column_index * simd_length;\n      return scalar_offset * n_components + comp * simd_length + simd_offset;\n\n    } else {\n      const unsigned int scalar_offset = row_starts_(row) + column_index;\n\n      return scalar_offset * n_components + comp;\n    }\n  }\n\n\n  template <int simd_length, typename MemorySpace>\n  template <unsigned int n_components>\n  DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int\n  SparsityPatternView<simd_length, MemorySpace>::transposed_offset(\n      const unsigned int row,\n      const unsigned int column_index,\n      const unsigned int component) const\n  {\n    AssertIndexRange(row, n_rows());\n    AssertIndexRange(column_index, row_length(row));\n    AssertIndexRange(component, n_components);\n\n    // Compute the transposed index from the (scalar) numbering stored in\n    // the sparsity pattern...\n    const unsigned int scalar_offset = offset(row, column_index);\n    const unsigned int transposed_scalar_offset =\n        indices_transposed_(scalar_offset);\n\n    // ... and reconstruct the proper index for a view with n_components:\n    const unsigned int j = column_indices_(scalar_offset);\n\n    unsigned int transposed_offset = transposed_scalar_offset;\n    if constexpr (n_components > 1) {\n      if (j < n_internal_dofs_) {\n        transposed_offset = //\n            transposed_offset / simd_length * simd_length * n_components +\n            transposed_offset % simd_length;\n        return transposed_offset + component * simd_length;\n\n      } else {\n\n        transposed_offset *= n_components;\n        return transposed_offset + component;\n      }\n\n    } else {\n\n      return transposed_offset;\n    }\n  }\n\n\n  template <int simd_length, typename MemorySpace>\n  template <unsigned int n_components>\n  DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int\n  SparsityPatternView<simd_length, MemorySpace>::offset_internal(\n      const unsigned int row, const unsigned int column_index) const\n  {\n    AssertIndexRange(row, n_rows());\n    AssertIndexRange(column_index, row_length(row));\n    AssertIndexRange(row, n_internal_dofs_);\n\n    const unsigned int simd_row = row / simd_length;\n\n    Assert(row % simd_length == 0,\n           dealii::ExcMessage(\n               \"Access only supported for rows at the SIMD granularity\"));\n\n    const unsigned int scalar_offset =\n        row_starts_(simd_row) + column_index * simd_length;\n\n    return scalar_offset * n_components;\n  }\n\n\n  template <int simd_length, typename MemorySpace>\n  template <unsigned int n_components>\n  DEAL_II_HOST_DEVICE_ALWAYS_INLINE const unsigned int *\n  SparsityPatternView<simd_length, MemorySpace>::transposed_offset_internal(\n      const unsigned int row, const unsigned int column_index) const\n  {\n    static_assert(n_components == 1,\n                  \"Vectorized transposed access to multiple components is not \"\n                  \"yet implemented.\");\n    AssertIndexRange(row, row_starts_.size() - 1);\n    AssertIndexRange(column_index, row_length(row));\n    AssertIndexRange(row, n_internal_dofs_);\n\n    const unsigned int simd_row = row / simd_length;\n\n    Assert(row % simd_length == 0,\n           dealii::ExcMessage(\n               \"Access only supported for rows at the SIMD granularity\"));\n\n    const unsigned int scalar_offset =\n        row_starts_(simd_row) + column_index * simd_length;\n\n    // n_components == 1\n    return indices_transposed_.data() + scalar_offset;\n  }\n\n\n  template <int simd_length, typename MemorySpace>\n  template <unsigned int n_components>\n  DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int\n  SparsityPatternView<simd_length, MemorySpace>::ghost_offset() const\n  {\n    const auto scalar_offset = row_starts_(n_locally_owned_dofs_);\n    return scalar_offset * n_components;\n  }\n\n\n#endif\n} // namespace ryujin\n"
  },
  {
    "path": "source/sparsity_pattern.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include \"simd.h\"\n#include \"sparsity_pattern.h\"\n\n#include <deal.II/base/vectorization.h>\n#include <deal.II/lac/dynamic_sparsity_pattern.h>\n#include <deal.II/lac/sparsity_pattern.h>\n\nnamespace ryujin\n{\n  template <int simd_length>\n  SparsityPattern<simd_length>::SparsityPattern()\n      : n_internal_dofs_(0)\n  {\n  }\n\n\n  template <int simd_length>\n  SparsityPattern<simd_length>::SparsityPattern(\n      const unsigned int n_internal_dofs,\n      const dealii::DynamicSparsityPattern &sparsity,\n      const std::shared_ptr<const dealii::Utilities::MPI::Partitioner>\n          &partitioner,\n      bool symmetrize_ghost_range)\n  {\n    reinit(n_internal_dofs, sparsity, partitioner, symmetrize_ghost_range);\n  }\n\n\n  template <int simd_length>\n  void SparsityPattern<simd_length>::reinit(\n      const unsigned int n_internal_dofs,\n      const dealii::DynamicSparsityPattern &dsp,\n      const std::shared_ptr<const dealii::Utilities::MPI::Partitioner>\n          &partitioner,\n      const bool symmetrize_ghost_range)\n  {\n    this->n_internal_dofs_ = n_internal_dofs;\n    this->n_locally_owned_dofs_ = partitioner->locally_owned_size();\n    this->partitioner_ = partitioner;\n\n    const auto n_locally_relevant_dofs =\n        partitioner->locally_owned_size() + partitioner->n_ghost_indices();\n\n    /*\n     * First, create a static sparsity pattern in local indexing.\n     */\n\n    dealii::DynamicSparsityPattern dsp_local(n_locally_relevant_dofs,\n                                             n_locally_relevant_dofs);\n    for (unsigned int i = 0; i < n_locally_relevant_dofs; ++i) {\n      const auto global_row = partitioner->local_to_global(i);\n      for (auto it = dsp.begin(global_row); it != dsp.end(global_row); ++it) {\n        const auto global_column = it->column();\n        const auto j = partitioner->global_to_local(global_column);\n        dsp_local.add(i, j);\n\n        if (symmetrize_ghost_range && //\n            i < n_locally_owned_dofs_ && j >= n_locally_owned_dofs_)\n          dsp_local.add(j, i);\n      }\n    }\n\n    dealii::SparsityPattern sparsity;\n    sparsity.copy_from(dsp_local);\n\n    Assert(n_internal_dofs <= sparsity.n_rows(), dealii::ExcInternalError());\n    Assert(n_internal_dofs % simd_length == 0, dealii::ExcInternalError());\n    Assert(n_internal_dofs <= n_locally_owned_dofs_,\n           dealii::ExcInternalError());\n    Assert(n_locally_owned_dofs_ <= sparsity.n_rows(),\n           dealii::ExcInternalError());\n\n    AssertThrow(\n        sparsity.n_nonzero_elements() <\n            std::numeric_limits<unsigned int>::max(),\n        dealii::ExcMessage(\n            \"Transposed indices only support up to 4 billion matrix entries \"\n            \"per MPI rank. Try to split into smaller problems with MPI\"));\n\n    /* Allocate memory: */\n\n    using KokkosHost = dealii::MemorySpace::Host::kokkos_space;\n    using KokkosDefault = dealii::MemorySpace::Default::kokkos_space;\n    using Aligned = Kokkos::MemoryTraits<Kokkos::Aligned>;\n\n    row_starts_host_ = Kokkos::View<unsigned int *, KokkosHost, Aligned>(\n        \"sparsity_pattern_row_starts\", sparsity.n_rows() + 1);\n\n    column_indices_host_ = Kokkos::View<unsigned int *, KokkosHost, Aligned>(\n        \"sparsity_pattern_column_indices\", sparsity.n_nonzero_elements());\n\n    indices_transposed_host_ =\n        Kokkos::View<unsigned int *, KokkosHost, Aligned>(\n            \"sparsity_pattern_column_indices\", sparsity.n_nonzero_elements());\n\n    /* Vectorized part: */\n\n    row_starts_host_[0] = 0;\n\n    unsigned int *col_ptr = column_indices_host_.data();\n    unsigned int *transposed_ptr = indices_transposed_host_.data();\n\n    for (unsigned int i = 0; i < n_internal_dofs; i += simd_length) {\n      auto jts = generate_iterators<simd_length>(\n          [&](auto k) { return sparsity.begin(i + k); });\n\n      for (; jts[0] != sparsity.end(i); increment_iterators(jts))\n        for (unsigned int k = 0; k < simd_length; ++k) {\n          const unsigned int column = jts[k]->column();\n          *col_ptr++ = column;\n          const std::size_t position = sparsity(column, i + k);\n          if (column < n_internal_dofs) {\n            const unsigned int my_row_length = sparsity.row_length(column);\n            const std::size_t position_diag = sparsity(column, column);\n            const std::size_t pos_within_row = position - position_diag;\n            const unsigned int simd_offset = column % simd_length;\n            *transposed_ptr++ = position - simd_offset * my_row_length -\n                                pos_within_row + simd_offset +\n                                pos_within_row * simd_length;\n          } else\n            *transposed_ptr++ = position;\n        }\n\n      row_starts_host_[i / simd_length + 1] =\n          col_ptr - column_indices_host_.data();\n    }\n\n    /* Rest: */\n\n    row_starts_host_[n_internal_dofs] =\n        row_starts_host_[n_internal_dofs / simd_length];\n\n    for (unsigned int i = n_internal_dofs; i < sparsity.n_rows(); ++i) {\n      for (auto j = sparsity.begin(i); j != sparsity.end(i); ++j) {\n        const unsigned int column = j->column();\n        *col_ptr++ = column;\n        const std::size_t position = sparsity(column, i);\n        if (column < n_internal_dofs) {\n          const unsigned int my_row_length = sparsity.row_length(column);\n          const std::size_t position_diag = sparsity(column, column);\n          const std::size_t pos_within_row = position - position_diag;\n          const unsigned int simd_offset = column % simd_length;\n          *transposed_ptr++ = position - simd_offset * my_row_length -\n                              pos_within_row + simd_offset +\n                              pos_within_row * simd_length;\n        } else\n          *transposed_ptr++ = position;\n      }\n      row_starts_host_[i + 1] = col_ptr - column_indices_host_.data();\n    }\n\n#ifdef DEBUG\n    const auto distance = std::distance(column_indices_host_.data(), col_ptr);\n    Assert(static_cast<std::size_t>(distance) == column_indices_host_.size(),\n           dealii::ExcInternalError());\n#endif\n\n    /*\n     * Compute the data exchange pattern:\n     */\n\n    receive_targets_.clear();\n    send_targets_.clear();\n    entries_to_be_sent_.clear();\n\n    if (sparsity.n_rows() > n_locally_owned_dofs_) {\n      const unsigned int mpi_tag =\n          dealii::Utilities::MPI::internal::Tags::partitioner_export_start + 0;\n\n      const auto &ghost_targets = partitioner->ghost_targets();\n      const auto &import_targets = partitioner->import_targets();\n      const auto &mpi_communicator = partitioner->get_mpi_communicator();\n\n      const unsigned int n_requests =\n          ghost_targets.size() + import_targets.size();\n      std::vector<MPI_Request> requests(n_requests);\n\n      /*\n       * Set up receive targets.\n       *\n       * We receive our local ghost rows from MPI ranks in the ghost range\n       * of the (scalar) partitioner. We receive our entire local ghost row\n       * from the owning MPI rank. We have to navigate one detail, though.\n       * Our local view of the ghost row is a subset of the full row of the\n       * owning rank. We thus have to communicate to the owning rank how\n       * many entries and what indices we are expecting.\n       *\n       * First, set up the receive_targets_ vector and send the cummulative\n       * row size to the owning MPI rank:\n       */\n\n      receive_targets_.resize(ghost_targets.size());\n      for (unsigned int p = 0; p < receive_targets_.size(); ++p) {\n        receive_targets_[p].first = ghost_targets[p].first;\n      }\n\n      {\n        /* Index into ghost targets: */\n        unsigned int ghost_targets_index = 0;\n        /* Current and previous index into ghost range of sparsity pattern: */\n        unsigned int index = 0;\n        unsigned int previous_index = 0;\n\n        unsigned int row_count = 0;\n        for (unsigned int i = n_locally_owned_dofs_; i < sparsity.n_rows();\n             ++i) {\n          index += sparsity.row_length(i);\n          ++row_count;\n          const auto ghost_target = ghost_targets[ghost_targets_index];\n          if (row_count == ghost_target.second) {\n            receive_targets_[ghost_targets_index].second = index;\n\n            unsigned int n_entries = index - previous_index;\n            const int ierr = MPI_Isend(\n                &n_entries,\n                1,\n                dealii::Utilities::MPI::mpi_type_id_for_type<unsigned int>,\n                ghost_target.first,\n                mpi_tag,\n                mpi_communicator,\n                &requests[ghost_targets_index]);\n            AssertThrowMPI(ierr);\n\n            /* Update indices: */\n            ++ghost_targets_index;\n            previous_index = index;\n            row_count = 0;\n          }\n        }\n\n        Assert(ghost_targets_index == partitioner->ghost_targets().size(),\n               dealii::ExcInternalError());\n      }\n\n\n      /*\n       * Set up send targets.\n       *\n       * First receive the number of entries that we will need to send.\n       */\n\n      std::vector<unsigned int> send_ranges(import_targets.size());\n      for (unsigned int p = 0; p < import_targets.size(); ++p) {\n        const int ierr = MPI_Irecv(\n            &send_ranges[p],\n            1,\n            dealii::Utilities::MPI::mpi_type_id_for_type<unsigned int>,\n            import_targets[p].first,\n            mpi_tag,\n            mpi_communicator,\n            &requests[ghost_targets.size() + p]);\n        AssertThrowMPI(ierr);\n      }\n\n      {\n        const int ierr =\n            MPI_Waitall(requests.size(), requests.data(), MPI_STATUSES_IGNORE);\n        AssertThrowMPI(ierr);\n      }\n\n      /*\n       * Now, that the owning rank knows the number of entries we request\n       * we can send the actual index pairs (i_global, j_global) that we\n       * require.\n       */\n\n      std::vector<dealii::types::global_dof_index> requested_entries;\n      {\n        /* Index into ghost targets: */\n        unsigned int ghost_targets_index = 0;\n\n        unsigned int row_count = 0;\n        for (unsigned int i = n_locally_owned_dofs_; i < sparsity.n_rows();\n             ++i) {\n          const auto i_global = partitioner_->local_to_global(i);\n          for (auto idx = sparsity.begin(i); idx != sparsity.end(i); ++idx) {\n            const unsigned int j = idx->column();\n            const auto j_global = partitioner_->local_to_global(j);\n            requested_entries.push_back(i_global);\n            requested_entries.push_back(j_global);\n          }\n\n          ++row_count;\n          if (row_count == ghost_targets[ghost_targets_index].second) {\n            /* Update indices: */\n            ++ghost_targets_index;\n            row_count = 0;\n          }\n        }\n\n        Assert(ghost_targets_index == partitioner->ghost_targets().size(),\n               dealii::ExcInternalError());\n\n#ifdef DEBUG\n        const auto ghost_offset = row_starts_host_(n_locally_owned_dofs_);\n        const auto n_nonzero_elements =\n            row_starts_host_(row_starts_host_.size() - 1);\n        Assert(requested_entries.size() ==\n                   2 * (n_nonzero_elements - ghost_offset),\n               dealii::ExcInternalError());\n#endif\n\n        for (unsigned int p = 0; p < receive_targets_.size(); ++p) {\n          const auto request_offset =\n              p == 0 ? 0 : receive_targets_[p - 1].second;\n          const auto request_size = receive_targets_[p].second - request_offset;\n\n          const int ierr =\n              MPI_Isend(requested_entries.data() + 2 * request_offset,\n                        2 * request_size,\n                        dealii::Utilities::MPI::mpi_type_id_for_type<\n                            dealii::types::global_dof_index>,\n                        receive_targets_[p].first,\n                        mpi_tag,\n                        mpi_communicator,\n                        &requests[p]);\n          AssertThrowMPI(ierr);\n        }\n      }\n\n      /*\n       * Accumulate all requests we received from other ranks:\n       */\n\n      send_targets_.resize(import_targets.size());\n\n      const unsigned int n_entries_to_be_sent =\n          std::accumulate(send_ranges.begin(), send_ranges.end(), 0);\n      std::vector<unsigned int> entries_buffer(2 * n_entries_to_be_sent /*!*/);\n\n      {\n        /* Index into entries_to_be_sent: */\n        unsigned int index = 0;\n\n        for (unsigned int p = 0; p < send_targets_.size(); ++p) {\n          const auto n_entries = send_ranges[p];\n\n          const int ierr = MPI_Irecv(\n              entries_buffer.data() + 2 * index /*!*/,\n              2 * n_entries /*!*/,\n              dealii::Utilities::MPI::mpi_type_id_for_type<unsigned int>,\n              import_targets[p].first,\n              mpi_tag,\n              mpi_communicator,\n              &requests[ghost_targets.size() + p]);\n          AssertThrowMPI(ierr);\n\n          index += n_entries;\n          send_targets_[p].first = import_targets[p].first;\n          send_targets_[p].second = index;\n        }\n      }\n\n      {\n        const int ierr =\n            MPI_Waitall(requests.size(), requests.data(), MPI_STATUSES_IGNORE);\n        AssertThrowMPI(ierr);\n      }\n\n      entries_to_be_sent_.clear();\n      for (unsigned int e = 0; e < n_entries_to_be_sent; ++e) {\n        const auto i_global = entries_buffer[2 * e];\n        const auto j_global = entries_buffer[2 * e + 1];\n        const auto i = partitioner_->global_to_local(i_global);\n        const auto j = partitioner_->global_to_local(j_global);\n\n        const std::size_t position = sparsity(i, j);\n        Assert(\n            position != sparsity.invalid_entry,\n            dealii::ExcMessage(\"Inconsistent global view of sparsity pattern: \"\n                               \"the requested column index is not present on \"\n                               \"the row stored on the owning MPI rank.\"));\n\n        const std::size_t position_diag = sparsity(i, i);\n        const std::size_t position_within_row = position - position_diag;\n\n        entries_to_be_sent_.emplace_back(i, position_within_row);\n      }\n    }\n\n    /*\n     * Copy data over to device and initialize the default host view:\n     */\n\n    row_starts_default_ = Kokkos::create_mirror_view_and_copy(\n        typename KokkosDefault::execution_space(), row_starts_host_);\n\n    column_indices_default_ = Kokkos::create_mirror_view_and_copy(\n        typename KokkosDefault::execution_space(), column_indices_host_);\n\n    indices_transposed_default_ = Kokkos::create_mirror_view_and_copy(\n        typename KokkosDefault::execution_space(), indices_transposed_host_);\n\n    SparsityPatternView<simd_length>::reinit(*this);\n  }\n} // namespace ryujin\n"
  },
  {
    "path": "source/state_vector.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2024 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"multicomponent_vector.h\"\n\n#include <deal.II/lac/la_parallel_block_vector.h>\n\nnamespace ryujin\n{\n#ifndef DOXYGEN\n  /* Forward declaration */\n  template <int dim, typename Number>\n  class OfflineData;\n#endif\n\n  /**\n   * A namespace for various vector type aliases.\n   *\n   * @ingroup Mesh\n   */\n  namespace Vectors\n  {\n    /**\n     * A scalar vector representing a single component given by a deal.II\n     * data type that is compatible with deal.II functions and methods and\n     * lives in the host memory space.\n     */\n    template <typename Number>\n    using ScalarHostVector = dealii::LinearAlgebra::distributed::Vector<Number>;\n\n\n    /**\n     * A block vector representing a multiple components given by a deal.II\n     * data type that is compatible with deal.II functions and methods and\n     * lives in the host memory space.\n     */\n    template <typename Number>\n    using BlockHostVector =\n        dealii::LinearAlgebra::distributed::BlockVector<Number>;\n\n\n    /**\n     * A scalar vector representing a single component.\n     */\n    template <typename Number>\n    using ScalarVector = MultiComponentVector<Number, 1>;\n\n\n    /**\n     * A compound state vector formed by a std::tuple consisting of the\n     * hyperbolic state vector @p U, precomputed values, and an \"parabolic\n     * state\" vector stored as a BlockVector. All of these vectors have in\n     * common that they are associated with a hyperbolic, or parabolic state\n     * and precomputed data (derived from the hyperbolic state) for point in\n     * time.\n     */\n    template <typename Number, unsigned int problem_dim, unsigned int prec_dim>\n    using StateVector = std::tuple<\n        MultiComponentVector<Number, problem_dim> /*U*/,\n        MultiComponentVector<Number, prec_dim> /*precomputed values*/,\n        BlockHostVector<Number> /*parabolic state vector*/>;\n\n\n    /**\n     * A small helper function that sets all values of the hyperbolic\n     * vector that are invalid after a hyperbolic substep to a NaN value.\n     * This includes:\n     *  - the entire precomputed state vector\n     *  - constrained degrees of freedom of the hyperbolic state vector\n     *  - the ghost range of the hyperbolic state vector\n     */\n    template <typename Number, int prob_dim, int prec_dim, typename OfflineData>\n    void debug_poison_invalid_values(\n        StateVector<Number, prob_dim, prec_dim> &state_vector [[maybe_unused]],\n        const OfflineData &offline_data [[maybe_unused]])\n    {\n#ifdef DEBUG\n      auto &[U, prec, V] = state_vector;\n\n      constexpr auto nan = std::numeric_limits<Number>::signaling_NaN();\n\n      const unsigned int n_owned = offline_data.n_locally_owned();\n      const unsigned int n_relevant = offline_data.n_locally_relevant();\n      const auto &partitioner = offline_data.scalar_partitioner();\n\n      for (unsigned int i = 0; i < n_owned; ++i) {\n        prec.write_tensor(dealii::Tensor<1, prec_dim, Number>() * nan, i);\n\n        if (!offline_data.affine_constraints().is_constrained(\n                partitioner->local_to_global(i)))\n          continue;\n        U.write_tensor(dealii::Tensor<1, prob_dim, Number>() * nan, i);\n      }\n\n      for (unsigned int i = n_owned; i < n_relevant; ++i) {\n        prec.write_tensor(dealii::Tensor<1, prec_dim, Number>() * nan, i);\n\n        U.write_tensor(dealii::Tensor<1, prob_dim, Number>() * nan, i);\n      }\n#endif\n    }\n\n  } // namespace Vectors\n} // namespace ryujin\n"
  },
  {
    "path": "source/stub_parabolic_module.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <hyperbolic_module.h>\n#include <initial_values.h>\n#include <mpi_ensemble.h>\n#include <offline_data.h>\n\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/timer.h>\n\nnamespace ryujin\n{\n  /**\n   * A stub parabolic solver that models the identity.\n   *\n   * @ingroup ParabolicModule\n   */\n  template <typename Description, int dim, typename Number>\n  class StubParabolicModule final : public dealii::ParameterAcceptor\n  {\n  public:\n    /**\n     * @name Typedefs and constexpr constants\n     */\n    //@{\n\n    using HyperbolicSystem = typename Description::HyperbolicSystem;\n\n    using View =\n        typename Description::template HyperbolicSystemView<dim, Number>;\n\n    using ParabolicSystem = typename Description::ParabolicSystem;\n\n    using StateVector = typename View::StateVector;\n\n    //@}\n    /**\n     * @name Constructor and setup\n     */\n    //@{\n\n    /**\n     * Constructor.\n     */\n    StubParabolicModule(\n        const MPIEnsemble & /*mpi_ensemle*/,\n        std::map<std::string, dealii::Timer> & /*computing_timer*/,\n        const OfflineData<dim, Number> & /*offline_data*/,\n        const HyperbolicSystem & /*hyperbolic_system*/,\n        const ParabolicSystem & /*parabolic_system*/,\n        const InitialValues<Description, dim, Number> & /*initial_values*/,\n        const std::string &subsection = \"StubParabolicModule\")\n        : ParameterAcceptor(subsection)\n    {\n    }\n\n    /**\n     * Prepare time stepping. A call to @p prepare() allocates temporary\n     * storage and is necessary before any of the following time-stepping\n     * functions can be called.\n     */\n    void prepare()\n    {\n      // do nothing\n    }\n\n    //@}\n    /**\n     * @name Functons for performing explicit time steps\n     */\n    //@{\n\n    /**\n     * (Re)initialize the parabolic state vector component of the state\n     * vector.\n     *\n     * @note This routine does not modify the hyperbolic state vector or\n     * the precomputed vector component.\n     */\n    void reinit_state_vector(StateVector & /*state_vector*/) const\n    {\n      // do nothing\n    }\n\n    /**\n     * This function preprocesses a given state vector @p U in preparation\n     * for a high order IMEX time step. This function exists because some\n     * time stepping variants have to precompute quantities before we can\n     * perform an IMEX step. In addition, this function is called whenever\n     * we perform a mesh transfer or output operation.\n     */\n    void prepare_state_vector(StateVector & /*state_vector*/,\n                              Number /*t*/) const\n    {\n      Assert(false,\n             dealii::ExcMessage(\"The parabolic system is the identity. This \"\n                                \"function should have never been called.\"));\n    }\n\n    /**\n     * Given a reference to a previous state vector @p old_U at time\n     * @p old_t and a time-step size @p tau perform an implicit backward\n     * euler step (and store the result in @p new_U).\n     *\n     * The function takes an optional array of states @p stage_U together\n     * with a an array of weights @p stage_weights to construct a modified\n     * high-order right-hand side / flux.\n     */\n    template <int stages>\n    void\n    backward_euler_step(const StateVector &old_state_vector,\n                        const Number /*old_t*/,\n                        std::array<std::reference_wrapper<const StateVector>,\n                                   stages> /*stage_state_vectors*/,\n                        const std::array<Number, stages> /*stage_weights*/,\n                        StateVector &new_state_vector,\n                        Number /*tau*/) const\n    {\n      Assert(false,\n             dealii::ExcMessage(\"The parabolic system is the identity. This \"\n                                \"function should have never been called.\"));\n\n      new_state_vector = old_state_vector;\n    }\n\n    /**\n     * Given a reference to a previous state vector @p old_U at time @p\n     * old_t and a time-step size @p tau perform an implicit Crank-Nicolson\n     * step (and store the result in @p new_U).\n     *\n     * This variant is used in the TimeIntegrator class for the Strang\n     * split variants.\n     */\n    void crank_nicolson_step(const StateVector &old_state_vector,\n                             const Number /*old_t*/,\n                             StateVector &new_state_vector,\n                             Number /*tau*/) const\n    {\n      Assert(false,\n             dealii::ExcMessage(\"The parabolic system is the identity. This \"\n                                \"function should have never been called.\"));\n\n      new_state_vector = old_state_vector;\n    }\n\n    /**\n     * Sets the invariant domain violation strategy.\n     */\n    void set_id_violation_strategy(\n        const IDViolationStrategy & /*new_strategy*/) const\n    {\n      // do nothing\n    }\n\n    //@}\n    /**\n     * @name Information and statistics\n     */\n    //@{\n\n    /**\n     * Print a status line with solver statistics. This function is used\n     * for constructing the status message displayed periodically in the\n     * TimeLoop.\n     */\n    void print_solver_statistics(std::ostream & /*output*/) const\n    {\n      // do nothing\n    }\n\n    /**\n     * The number of restarts signalled by the step() function.\n     */\n    unsigned int n_restarts() const\n    {\n      return 0;\n    }\n\n    /**\n     * The number of corrections performed by the step() function. This\n     * function exists to mirror the ParabolicModule interface and will\n     * always return 0.\n     */\n    unsigned int n_corrections() const\n    {\n      return 0;\n    }\n\n    /**\n     * The number of ID violation warnings encounterd in the step()\n     * function.\n     */\n    unsigned int n_warnings() const\n    {\n      return 0;\n    }\n\n    //@}\n  };\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/stub_parabolic_system.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <convenience_macros.h>\n\n#include <deal.II/base/parameter_acceptor.h>\n\nnamespace ryujin\n{\n  /**\n   * The parabolic subsystem of the compressible Euler equations. This is\n   * just the idenity operator.\n   *\n   * @ingroup ParabolicModule\n   */\n  class StubParabolicSystem final : public dealii::ParameterAcceptor\n  {\n  public:\n    /**\n     * The name of the hyperbolic system as a string.\n     */\n    static inline const std::string problem_name = \"Identity\";\n\n    /**\n     * This parabolic subsystem represents an identity.\n     */\n    static constexpr bool is_identity = true;\n\n    /**\n     * Constructor.\n     */\n    StubParabolicSystem(const std::string &subsection = \"/ParabolicSystem\");\n\n    ACCESSOR_READ_ONLY(parabolic_component_names);\n\n  private:\n    const std::vector<std::string> parabolic_component_names_;\n  }; /* StubParabolicSystem */\n\n\n  /*\n   * -------------------------------------------------------------------------\n   * Inline definitions\n   * -------------------------------------------------------------------------\n   */\n\n\n  inline StubParabolicSystem::StubParabolicSystem(const std::string &subsection)\n      : ParameterAcceptor(subsection)\n  {\n  }\n} // namespace ryujin\n"
  },
  {
    "path": "source/tensor_product_point_kernels.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception or LGPL-2.1-or-later\n// Copyright (C) 2024 - 2024 by Maximilian Bergbauer\n// Copyright (C) 2024 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <deal.II/base/config.h>\n\n#include <deal.II/base/ndarray.h>\n#include <deal.II/base/polynomial.h>\n#include <deal.II/base/utilities.h>\n#include <deal.II/matrix_free/tensor_product_point_kernels.h>\n\n#include <deal.II/matrix_free/shape_info.h>\n\nnamespace ryujin\n{\n\n  namespace internal\n  {\n    /**\n     * This is a copy of the integrate_add_tensor_product_value_linear()\n     * function shipped with deal.II that fixes a compilation error when\n     * instantiating with float.\n     *\n     * @ingroup Mesh\n     */\n    template <int dim, typename Number, typename Number2, bool add>\n    inline void integrate_add_tensor_product_value_linear(\n        const Number2 &value,\n        Number2 *values,\n        const dealii::Point<dim, Number> &p)\n    {\n      static_assert(dim >= 0 && dim <= 3, \"Only dim=0,1,2,3 implemented\");\n\n      if (dim == 0) {\n        if (add)\n          values[0] += value;\n        else\n          values[0] = value;\n      } else if (dim == 1) {\n        const auto x0 = Number(1.) - p[0], x1 = p[0];\n\n        if (add) {\n          values[0] += value * x0;\n          values[1] += value * x1;\n        } else {\n          values[0] = value * x0;\n          values[1] = value * x1;\n        }\n      } else if (dim == 2) {\n        const auto x0 = Number(1.) - p[0], x1 = p[0], y0 = Number(1.) - p[1],\n                   y1 = p[1];\n\n        const auto test_value_y0 = value * y0;\n        const auto test_value_y1 = value * y1;\n\n        if (add) {\n          values[0] += x0 * test_value_y0;\n          values[1] += x1 * test_value_y0;\n          values[2] += x0 * test_value_y1;\n          values[3] += x1 * test_value_y1;\n        } else {\n          values[0] = x0 * test_value_y0;\n          values[1] = x1 * test_value_y0;\n          values[2] = x0 * test_value_y1;\n          values[3] = x1 * test_value_y1;\n        }\n      } else if (dim == 3) {\n        const auto x0 = Number(1.) - p[0], x1 = p[0], y0 = Number(1.) - p[1],\n                   y1 = p[1], z0 = Number(1.) - p[2], z1 = p[2];\n\n        const auto test_value_z0 = value * z0;\n        const auto test_value_z1 = value * z1;\n\n        const auto test_value_y00 = test_value_z0 * y0;\n        const auto test_value_y01 = test_value_z0 * y1;\n        const auto test_value_y10 = test_value_z1 * y0;\n        const auto test_value_y11 = test_value_z1 * y1;\n\n        if (add) {\n          values[0] += x0 * test_value_y00;\n          values[1] += x1 * test_value_y00;\n          values[2] += x0 * test_value_y01;\n          values[3] += x1 * test_value_y01;\n          values[4] += x0 * test_value_y10;\n          values[5] += x1 * test_value_y10;\n          values[6] += x0 * test_value_y11;\n          values[7] += x1 * test_value_y11;\n        } else {\n          values[0] = x0 * test_value_y00;\n          values[1] = x1 * test_value_y00;\n          values[2] = x0 * test_value_y01;\n          values[3] = x1 * test_value_y01;\n          values[4] = x0 * test_value_y10;\n          values[5] = x1 * test_value_y10;\n          values[6] = x0 * test_value_y11;\n          values[7] = x1 * test_value_y11;\n        }\n      }\n    }\n\n    template <bool is_linear, int dim, typename Number, typename Number2>\n    inline void integrate_tensor_product_value(\n        const dealii::ndarray<Number, 2, dim> *shapes,\n        const unsigned int n_shapes,\n        const Number2 &value,\n        Number2 *values,\n        const dealii::Point<dim, Number> &p,\n        const bool do_add)\n    {\n      if (do_add) {\n        if (is_linear)\n          integrate_add_tensor_product_value_linear<dim, Number, Number2, true>(\n              value, values, p);\n        else\n          dealii::internal::integrate_add_tensor_product_value_shapes<dim,\n                                                                      Number,\n                                                                      Number2,\n                                                                      true>(\n              shapes, n_shapes, value, values);\n      } else {\n        if (is_linear)\n          integrate_add_tensor_product_value_linear<dim,\n                                                    Number,\n                                                    Number2,\n                                                    false>(value, values, p);\n        else\n          dealii::internal::integrate_add_tensor_product_value_shapes<dim,\n                                                                      Number,\n                                                                      Number2,\n                                                                      false>(\n              shapes, n_shapes, value, values);\n      }\n    }\n  } // end of namespace internal\n\n} // namespace ryujin\n"
  },
  {
    "path": "source/time_integrator.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2023 by the ryujin authors\n//\n\n#include \"time_integrator.template.h\"\n#include <instantiate.h>\n\nnamespace ryujin\n{\n  /* instantiations */\n  template class TimeIntegrator<Description, 1, NUMBER>;\n  template class TimeIntegrator<Description, 2, NUMBER>;\n  template class TimeIntegrator<Description, 3, NUMBER>;\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/time_integrator.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2022 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"convenience_macros.h\"\n#include \"hyperbolic_module.h\"\n#include \"mpi_ensemble.h\"\n#include \"observer_pointer.h\"\n#include \"offline_data.h\"\n#include \"parabolic_module.h\"\n#include \"patterns_conversion.h\"\n\n#include <numbers>\n\nnamespace ryujin\n{\n  /**\n   * Controls the chosen invariant domain / CFL recovery strategy.\n   *\n   * @ingroup TimeLoop\n   */\n  enum class CFLRecoveryStrategy {\n    /**\n     * Step with the chosen \"cfl max\" value and do nothing in case an\n     * invariant domain and or CFL condition violation is detected.\n     */\n    none,\n\n    /**\n     * Step with the chosen \"cfl max\" value and, in case an invariant\n     * domain or \"CFL condition\" violation is detected, the time step is\n     * repeated with \"cfl min\". If this is unsuccessful as well, a warning\n     * is emitted.\n     */\n    bang_bang_control,\n\n    /**\n     * Adaptive strategy that similarly to \"bang bang control\" first steps\n     * with the chosen \"cfl max\" value, and, in case of an invariant domain\n     * violation or a \"CFL condition\" violation restarts the time step\n     * using a tau_max hint coming from the sub step where we encountered a\n     * problem. This is the default strategy.\n     */\n    cruise_control,\n  };\n\n\n  /**\n   * Controls the chosen time-stepping scheme.\n   *\n   * @ingroup TimeLoop\n   */\n  enum class TimeSteppingScheme {\n    /**\n     * The strong stability preserving Runge Kutta method of order 2,\n     * SSPRK(2,2;1/2), with the following butcher tableau\n     * \\f{align*}\n     * \\begin{array}{c|ccc}\n     *   0            & 0 \\\\\n     *   \\tfrac{1}{2} & \\tfrac{1}{2} & 0 \\\\\n     *   \\hline\n     *   1            & 1  & 0\n     * \\end{array}\n     * \\f}\n     */\n    ssprk_22,\n    /**\n     * The strong stability preserving Runge Kutta method of order 3,\n     * SSPRK(3,3;1/3), with the following butcher tableau\n     * \\f{align*}\n     * \\begin{array}{c|ccc}\n     *   0            & 0 \\\\\n     *   1            & 1            & 0 \\\\\n     *   \\tfrac{1}{2} & \\tfrac{1}{4} & \\tfrac{1}{4} & 0\\\\\n     *   \\hline\n     *   1            & \\tfrac{1}{6} & \\tfrac{1}{6} & \\tfrac{2}{3}\n     * \\end{array}\n     * \\f}\n     */\n    ssprk_33,\n\n    /**\n     * The explicit Runge-Kutta method RK(1,1;1), aka a simple, forward\n     * Euler step.\n     */\n    erk_11,\n\n    /**\n     * The explicit Runge-Kutta method RK(2,2;1) with the butcher tableau\n     * \\f{align*}\n     * \\begin{array}{c|ccc}\n     *   0            & 0 \\\\\n     *   \\tfrac{1}{2} & \\tfrac{1}{2} & 0 \\\\\n     *   \\hline\n     *   1            & 0  & 1\n     * \\end{array}\n     * \\f}\n     */\n    erk_22,\n\n    /**\n     * The explicit Runge-Kutta method RK(3,3;1) with the butcher tableau\n     * \\f{align*}\n     * \\begin{array}{c|ccc}\n     *   0            & 0 \\\\\n     *   \\tfrac{1}{3} & \\tfrac{1}{3} & 0 \\\\\n     *   \\tfrac{2}{3} & 0            & \\tfrac{2}{3} & 0 \\\\\n     *   \\hline\n     *   1            & \\tfrac{1}{4} & 0            & \\tfrac{3}{4}\n     * \\end{array}\n     * \\f}\n     */\n    erk_33,\n\n    /**\n     * The explicit Runge-Kutta method RK(4,3;1) with the butcher tableau\n     * \\f{align*}\n     * \\begin{array}{c|ccc}\n     *   0            & 0 \\\\\n     *   \\tfrac{1}{4} & \\tfrac{1}{4} & 0 \\\\\n     *   \\tfrac{1}{2} & 0            & \\tfrac{1}{2} & 0 \\\\\n     *   \\tfrac{3}{4} & 0            & \\tfrac{1}{4} & \\tfrac{1}{2}  & 0 \\\\\n     *   \\hline\n     *   1            & 0            & \\tfrac{2}{3} & -\\tfrac{1}{3} &\n     * \\tfrac{2}{3} \\end{array} \\f}\n     */\n    erk_43,\n\n    /**\n     * The explicit Runge-Kutta method RK(5,4;1) with the butcher tableau\n     * TODO\n     */\n    erk_54,\n\n    /**\n     * A Strang split using ssprk 33 for the hyperbolic subproblem and\n     * Crank-Nicolson for the parabolic subproblem\n     */\n    strang_ssprk_33_cn,\n\n    /**\n     * A Strang split using erk 33 for the hyperbolic subproblem and\n     * Crank-Nicolson for the parabolic subproblem\n     */\n    strang_erk_33_cn,\n\n    /**\n     * A Strang split using erk 43 for the hyperbolic subproblem and\n     * Crank-Nicolson for the parabolic subproblem\n     */\n    strang_erk_43_cn,\n\n    /**\n     * A Euler IMEX splitting. This is the low order IMEX method: it performs a\n     * forward Euler time step for the hyperbolic subproblem and then a backward\n     * Euler time step for the parabolic subproblem. */\n    imex_11,\n\n    /**\n     * An implicit-explicit method that utilizes the Heun's second order\n     * explicit Runge-Kutta\n     * scheme with Butcher tableau\n     *\n     * \\f{align*}\n     * \\begin{array}{c|ccc}\n     *   0            & 0 \\\\\n     *   1 & 0.5 & 0 \\\\\n     *   0.5 & 0        &  2 \\\\\n     *  \\end{array} \\f}\n     *\n     * to solve the explicit subproblem and the two-stage Crank-Nicolson scheme\n     * diagonally implicit Runge-Kutta scheme with Butcher tableau \\f{align*}\n     *\n     * \\begin{array}{c|ccc}\n     *   0            & 0 \\\\\n     *   0.5 & 0 & \\tfrac{1}{2} \\\\\n     *   1 & 0    &  1 \\\\\n     *  \\end{array} \\f}\n     *\n     * to solve the parabolic subproblem.\n     */\n    imex_22,\n\n    /**\n     * An implicit-explicit method that utilizes the Heun's second order\n     * explicit Runge-Kutta\n     * scheme with Butcher tableau\n     *\n     * \\f{align*}\n     * \\begin{array}{c|cccc}\n     *   0  & 0 \\\\\n     *   \\tfrace{1}{3} & \\tfrace{1}{3} & 0 \\\\\n     *   \\tfrace{2}{3} & 0  &  \\tfrace{2}{3} & 0 \\\\\n          1 & \\tfrace{1}{4} & 0 & \\tfrace{3}{4}\n     *  \\end{array} \\f}\n     *\n     * to solve the explicit subproblem and the two-stage Crank-Nicolson scheme\n     * diagonally implicit Runge-Kutta scheme with Butcher tableau \\f{align*}\n     *\n     * \\begin{array}{c|cccc}\n     *   0   & 0 \\\\\n     *   \\tfrace{1}{3} & \\tfrace{1}{3} - \\gamma & \\gamma \\\\\n     *   \\tfrace{2}{3} & \\gamma   &  \\tfrace{2}{3} - 2 \\gamma & \\gamma \\\\\n    *     1 & \\tfrace{1}{4} & 0 & \\tfrace{3}{4}\n     *  \\end{array} \\f}\n     *\n     * with \\gamma = \\tfrace{1}{2} + \\tfrace{1}{2\\sqrt(3)}\n     *\n     * to solve the parabolic subproblem.\n     */\n    imex_33,\n  };\n} // namespace ryujin\n\n#ifndef DOXYGEN\nDECLARE_ENUM(\n    ryujin::CFLRecoveryStrategy,\n    LIST({ryujin::CFLRecoveryStrategy::none, \"none\"},\n         {ryujin::CFLRecoveryStrategy::bang_bang_control, \"bang bang control\"},\n         {ryujin::CFLRecoveryStrategy::cruise_control, \"cruise control\"}, ));\n\nDECLARE_ENUM(\n    ryujin::TimeSteppingScheme,\n    LIST({ryujin::TimeSteppingScheme::ssprk_22, \"ssprk 22\"},\n         {ryujin::TimeSteppingScheme::ssprk_33, \"ssprk 33\"},\n         {ryujin::TimeSteppingScheme::erk_11, \"erk 11\"},\n         {ryujin::TimeSteppingScheme::erk_22, \"erk 22\"},\n         {ryujin::TimeSteppingScheme::erk_33, \"erk 33\"},\n         {ryujin::TimeSteppingScheme::erk_43, \"erk 43\"},\n         {ryujin::TimeSteppingScheme::erk_54, \"erk 54\"},\n         {ryujin::TimeSteppingScheme::strang_ssprk_33_cn, \"strang ssprk 33 cn\"},\n         {ryujin::TimeSteppingScheme::strang_erk_33_cn, \"strang erk 33 cn\"},\n         {ryujin::TimeSteppingScheme::strang_erk_43_cn, \"strang erk 43 cn\"},\n         {ryujin::TimeSteppingScheme::imex_11, \"imex 11\"},\n         {ryujin::TimeSteppingScheme::imex_22, \"imex 22\"},\n         {ryujin::TimeSteppingScheme::imex_33, \"imex 33\"}));\n#endif\n\nnamespace ryujin\n{\n  /**\n   * The TimeIntegrator class implements IMEX timestepping strategies based\n   * on explicit and diagonally-implicit Runge Kutta schemes.\n   *\n   * @ingroup TimeLoop\n   */\n  template <typename Description, int dim, typename Number = double>\n  class TimeIntegrator final : public dealii::ParameterAcceptor\n  {\n  public:\n    /**\n     * @name Typedefs and constexpr constants\n     */\n    //@{\n\n    using HyperbolicSystem = typename Description::HyperbolicSystem;\n\n    using View =\n        typename Description::template HyperbolicSystemView<dim, Number>;\n\n    using ParabolicSystem = typename Description::ParabolicSystem;\n\n    using StateVector = typename View::StateVector;\n\n    //@}\n    /**\n     * @name Constructor and setup\n     */\n    //@{\n\n    /**\n     * Constructor.\n     */\n    TimeIntegrator(\n        const MPIEnsemble &mpi_ensemble,\n        const OfflineData<dim, Number> &offline_data,\n        const HyperbolicModule<Description, dim, Number> &hyperbolic_module,\n        const ParabolicModule<Description, dim, Number> &parabolic_module,\n        const std::string &subsection = \"/TimeIntegrator\");\n\n    /**\n     * Prepare time integration. A call to prepare() allocates temporary\n     * storage and is necessary before any of the following time-stepping\n     * functions can be called.\n     */\n    void prepare();\n\n    //@}\n    /**\n     * @name Functions for performing explicit time steps\n     */\n    //@{\n\n    /**\n     * This function preprocesses a given state vector @p U for time\n     * stepping. It has to be called prior to step() to ensure that\n     * precomputed values are in place.\n     *\n     * @note Internally, this function first calls the\n     * ParabolicModule::prepare_state_vector() and afterwards\n     * HyperbolicModule::prepare_state_vector().\n     */\n    void prepare_state_vector(StateVector &state_vector, Number t) const;\n\n    /**\n     * Given a reference to a previous state vector U performs an explicit\n     * time step (and store the result in U). The function returns the\n     * chosen time step size tau. The time step size tau is selected such\n     * that $t + tau <= t_final$.\n     *\n     * @note This function switches between different Runge-Kutta methods\n     * depending on chosen runtime parameters.\n     *\n     * @note Depending on chosen run time parameters different CFL\n     * adaptation and recovery strategies for invariant domain violations\n     * are used.\n     *\n     * @pre The @p state_vector has to be prepared with the\n     * prepare_state_vector() function prior to calling the step()\n     * function.\n     */\n    Number step(StateVector &state_vector,\n                Number t,\n                Number t_final = std::numeric_limits<Number>::max());\n\n    /**\n     * The selected time-stepping scheme.\n     */\n    ACCESSOR_READ_ONLY(time_stepping_scheme);\n\n    /**\n     * The eficiency of the selected time-stepping scheme expressed as the\n     * ratio of step size of the combined method to step size of an\n     * elementary forward Euler step. For example, SSPRK33 has an\n     * efficiency ratio of 1 whereas ERK33 has an efficiency ratio of 3.\n     */\n    ACCESSOR_READ_ONLY(efficiency);\n\n  protected:\n    /**\n     * Given a reference to a previous state vector U performs an explicit\n     * second-order strong-stability preserving Runge-Kutta SSPRK(2,2;1/2)\n     * time step (and store the result in U). The function returns the\n     * chosen time step size tau, which is guaranteed to be less than or\n     * equal to the parameter @p tau_max.\n     */\n    Number step_ssprk_22(StateVector &state_vector, Number t, Number tau_max);\n\n    /**\n     * Given a reference to a previous state vector U performs an explicit\n     * third-order strong-stability preserving Runge-Kutta SSPRK(3,3;1/3)\n     * time step (and store the result in U). The function returns the\n     * chosen time step size tau, which is guaranteed to be less than or\n     * equal to the parameter @p tau_max.\n     */\n    Number step_ssprk_33(StateVector &state_vector, Number t, Number tau_max);\n\n    /**\n     * Given a reference to a previous state vector U performs an explicit\n     * first-order Euler step ERK(1,1;1) time step (and store the result\n     * in U). The function returns the chosen time step size tau, which is\n     * guaranteed to be less than or equal to the parameter @p tau_max.\n     */\n    Number step_erk_11(StateVector &state_vector, Number t, Number tau_max);\n\n    /**\n     * Given a reference to a previous state vector U performs an explicit\n     * second-order Runge-Kutta ERK(2,2;1) time step (and store the result\n     * in U). The function returns the chosen time step size tau, which is\n     * guaranteed to be less than or equal to the parameter @p tau_max.\n     */\n    Number step_erk_22(StateVector &state_vector, Number t, Number tau_max);\n\n    /**\n     * Given a reference to a previous state vector U performs an explicit\n     * third-order Runge-Kutta ERK(3,3;1) time step (and store the result\n     * in U). The function returns the chosen time step size tau, which is\n     * guaranteed to be less than or equal to the parameter @p tau_max.\n     */\n    Number step_erk_33(StateVector &state_vector, Number t, Number tau_max);\n\n    /**\n     * Given a reference to a previous state vector U performs an explicit\n     * 4 stage third-order Runge-Kutta ERK(4,3;1) time step (and store the\n     * result in U). The function returns the chosen time step size tau,\n     * which is guaranteed to be less than or equal to the parameter @p\n     * tau_max.\n     */\n    Number step_erk_43(StateVector &state_vector, Number t, Number tau_max);\n\n    /**\n     * Given a reference to a previous state vector U performs an explicit\n     * 4 stage fourth-order Runge-Kutta ERK(5,4;1) time step (and store\n     * the result in U). The function returns the chosen time step size\n     * tau, which is guaranteed to be less than or equal to the parameter\n     * @p tau_max.\n     */\n    Number step_erk_54(StateVector &state_vector, Number t, Number tau_max);\n\n    /**\n     * Given a reference to a previous state vector U performs a combined\n     * explicit implicit Strang split using a third-order Runge-Kutta\n     * ERK(3,3;1/3) time step and an implicit Crank-Nicolson step (and\n     * store the result in U). The function returns the chosen time step\n     * size tau, which is guaranteed to be less than or equal to the\n     * parameter @p tau_max.\n     */\n    Number step_strang_ssprk_33_cn(StateVector &state_vector,\n                                   Number t,\n                                   Number tau_max);\n\n    /**\n     * Given a reference to a previous state vector U performs a combined\n     * explicit implicit Strang split using a third-order Runge-Kutta\n     * ERK(3,3;1) time step and an implicit Crank-Nicolson step (and store\n     * the result in U). The function returns the chosen time step size\n     * tau, which is guaranteed to be less than or equal to the parameter\n     * @p tau_max.\n     */\n    Number\n    step_strang_erk_33_cn(StateVector &state_vector, Number t, Number tau_max);\n\n    /**\n     * Given a reference to a previous state vector U performs a combined\n     * explicit implicit Strang split using a third-order Runge-Kutta\n     * ERK(4,3;1) time step and an implicit Crank-Nicolson step (and store\n     * the result in U). The function returns the chosen time step size\n     * tau, which is guaranteed to be less than or equal to the parameter\n     * @p tau_max.\n     */\n    Number\n    step_strang_erk_43_cn(StateVector &state_vector, Number t, Number tau_max);\n\n    /** Given a reference to a previous state vector U, performs an\n     * implicit-explicit step IMEX(1,1;1) using a forward euler scheme for the\n     * hyperbolic subproblem and backward euler scheme for the parabolic\n     * subproblem. */\n    Number step_imex_11(StateVector &state_vector, Number t, Number tau_max);\n\n    /**\n     * Given a reference to a previous state vector U performs an\n     * implicit-explicit IMEX(2,2;1) step using a two stage midpoint rule for\n     * the hyperbolic subproblem and a two stage midpoint rule for the parabolic\n     * subproblem. The function returns the chosen time step size tau.\n     */\n    Number step_imex_22(StateVector &state_vector, Number t, Number tau_max);\n\n    /**\n     * Given a reference to a previous state vector U performs an\n     * implicit-explicit IMEX(3,3;1) step using a three stage ERK tableau for\n     * the hyperbolic subproblem and a three stage DIRK tableau for the\n     * parabolic subproblem. The function returns the chosen time step size tau.\n     */\n    Number step_imex_33(StateVector &state_vector, Number t, Number tau_max);\n\n  private:\n    //@}\n    /**\n     * @name Run time options\n     */\n    //@{\n\n    Number cfl_min_;\n    Number cfl_max_;\n\n    CFLRecoveryStrategy cfl_recovery_strategy_;\n\n    Number acceptable_tau_max_ratio_;\n    Number tau_max_;\n\n    TimeSteppingScheme time_stepping_scheme_;\n    Number efficiency_;\n\n    //@}\n\n    //@}\n    /**\n     * @name Internal data\n     */\n    //@{\n\n    const MPIEnsemble &mpi_ensemble_;\n\n    dealii::ObserverPointer<const OfflineData<dim, Number>> offline_data_;\n    dealii::ObserverPointer<const HyperbolicModule<Description, dim, Number>>\n        hyperbolic_module_;\n    dealii::ObserverPointer<const ParabolicModule<Description, dim, Number>>\n        parabolic_module_;\n\n    std::vector<StateVector> temp_;\n\n    //@}\n  };\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/time_integrator.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2022 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include \"time_integrator.h\"\n\nnamespace ryujin\n{\n  using namespace dealii;\n\n  template <typename StateVector, typename Number>\n  void\n  sadd(StateVector &dst, const Number s, const Number b, const StateVector &src)\n  {\n    auto &dst_U = std::get<0>(dst);\n    auto &src_U = std::get<0>(src);\n    dst_U.zero_out_ghost_values();\n    dst_U.sadd(s, b, src_U);\n\n    auto &dst_V = std::get<2>(dst);\n    auto &src_V = std::get<2>(src);\n    dst_V.zero_out_ghost_values();\n    dst_V.sadd(s, b, src_V);\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  TimeIntegrator<Description, dim, Number>::TimeIntegrator(\n      const MPIEnsemble &mpi_ensemble,\n      const OfflineData<dim, Number> &offline_data,\n      const HyperbolicModule<Description, dim, Number> &hyperbolic_module,\n      const ParabolicModule<Description, dim, Number> &parabolic_module,\n      const std::string &subsection /*= \"TimeIntegrator\"*/)\n      : ParameterAcceptor(subsection)\n      , mpi_ensemble_(mpi_ensemble)\n      , offline_data_(&offline_data)\n      , hyperbolic_module_(&hyperbolic_module)\n      , parabolic_module_(&parabolic_module)\n  {\n    cfl_min_ = Number(0.45);\n    add_parameter(\n        \"cfl min\",\n        cfl_min_,\n        \"Minimal admissible relative CFL constant. How this parameter is used \"\n        \"depends on the chosen CFL recovery strategy\");\n\n    cfl_max_ = Number(0.90);\n    add_parameter(\n        \"cfl max\",\n        cfl_max_,\n        \"Maximal admissible relative CFL constant. How this parameter is used \"\n        \"depends on the chosen CFL recovery strategy\");\n\n    cfl_recovery_strategy_ = CFLRecoveryStrategy::cruise_control;\n    add_parameter(\"cfl recovery strategy\",\n                  cfl_recovery_strategy_,\n                  \"CFL/invariant domain violation recovery strategy: none, \"\n                  \"bang bang control, cruise control\");\n\n    acceptable_tau_max_ratio_ = Number(2.0);\n    add_parameter(\"acceptable tau_max ratio\",\n                  acceptable_tau_max_ratio_,\n                  \"Maximal acceptable discrepancy between computed tau_max of \"\n                  \"a (sub)step and enforced time-step size tau. If the ratio \"\n                  \"is violated then a restart will be singnalled.\");\n\n    tau_max_ = std::numeric_limits<Number>::max();\n    add_parameter(\"tau_max\", tau_max_, \"Largest time step size allowed.\");\n\n    if (ParabolicSystem::is_identity)\n      time_stepping_scheme_ = TimeSteppingScheme::erk_33;\n    else\n      time_stepping_scheme_ = TimeSteppingScheme::strang_erk_33_cn;\n    add_parameter(\"time stepping scheme\",\n                  time_stepping_scheme_,\n                  \"Time stepping scheme: ssprk 22, ssprk 33, erk 11, erk 22, \"\n                  \"erk 33, erk 43, erk \"\n                  \"54, strang ssprk 33 cn, strang erk 33 cn, strang erk 43 cn, \"\n                  \"imex 11, imex 22, imex 33\");\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void TimeIntegrator<Description, dim, Number>::prepare()\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"TimeIntegrator<dim, Number>::prepare()\" << std::endl;\n#endif\n\n    /* Resize temporary storage to appropriate sizes: */\n\n    switch (time_stepping_scheme_) {\n    case TimeSteppingScheme::ssprk_22:\n      temp_.resize(2);\n      efficiency_ = 1.;\n      break;\n    case TimeSteppingScheme::ssprk_33:\n      temp_.resize(2);\n      efficiency_ = 1.;\n      break;\n    case TimeSteppingScheme::erk_11:\n      temp_.resize(1);\n      efficiency_ = 1.;\n      break;\n    case TimeSteppingScheme::erk_22:\n      temp_.resize(2);\n      efficiency_ = 2.;\n      break;\n    case TimeSteppingScheme::erk_33:\n      temp_.resize(3);\n      efficiency_ = 3.;\n      break;\n    case TimeSteppingScheme::erk_43:\n      temp_.resize(4);\n      efficiency_ = 4.;\n      break;\n    case TimeSteppingScheme::erk_54:\n      temp_.resize(5);\n      efficiency_ = 5.;\n      break;\n    case TimeSteppingScheme::strang_ssprk_33_cn:\n      temp_.resize(3);\n      efficiency_ = 2.;\n      break;\n    case TimeSteppingScheme::strang_erk_33_cn:\n      temp_.resize(4);\n      efficiency_ = 6.;\n      break;\n    case TimeSteppingScheme::strang_erk_43_cn:\n      temp_.resize(4);\n      efficiency_ = 8.;\n      break;\n    case TimeSteppingScheme::imex_11:\n      temp_.resize(2);\n      efficiency_ = 1.;\n      break;\n    case TimeSteppingScheme::imex_22:\n      temp_.resize(4);\n      efficiency_ = 2.;\n      break;\n    case TimeSteppingScheme::imex_33:\n      temp_.resize(6);\n      efficiency_ = 3.;\n      break;\n    }\n\n    /* Initialize temporary vectors: */\n\n    for (auto &it : temp_) {\n      hyperbolic_module_->reinit_state_vector(it);\n      parabolic_module_->reinit_state_vector(it);\n    }\n\n    /* Reset CFL to starting value, set maximal acceptable tau_max ratio: */\n\n    AssertThrow(cfl_min_ > 0., ExcMessage(\"cfl min must be a positive value\"));\n    AssertThrow(cfl_max_ >= cfl_min_,\n                ExcMessage(\"cfl max must be greater than or equal to cfl min\"));\n\n    AssertThrow(\n        acceptable_tau_max_ratio_ >= 1.0,\n        ExcMessage(\n            \"acceptable tau_max ratio must be greater than or equal to 1.\"));\n\n    hyperbolic_module_->set_cfl(cfl_max_);\n    hyperbolic_module_->set_acceptable_tau_max_ratio(acceptable_tau_max_ratio_);\n\n    const auto check_whether_timestepping_makes_sense = [&]() {\n      /*\n       * Make sure the user selects an appropriate time-stepping scheme.\n       */\n\n      switch (time_stepping_scheme_) {\n      case TimeSteppingScheme::ssprk_22:\n        [[fallthrough]];\n      case TimeSteppingScheme::ssprk_33:\n        [[fallthrough]];\n      case TimeSteppingScheme::erk_11:\n        [[fallthrough]];\n      case TimeSteppingScheme::erk_22:\n        [[fallthrough]];\n      case TimeSteppingScheme::erk_33:\n        [[fallthrough]];\n      case TimeSteppingScheme::erk_43:\n        [[fallthrough]];\n      case TimeSteppingScheme::erk_54: {\n        AssertThrow(\n            ParabolicSystem::is_identity,\n            dealii::ExcMessage(\n                \"The selected equation consists of a hyperbolic and nontrivial \"\n                \"parabolic subsystem and requires an IMEX timestepping \"\n                \"scheme such as »strang erk 33 cn«.\"));\n        break;\n      }\n      case TimeSteppingScheme::imex_11:\n        [[fallthrough]];\n      case TimeSteppingScheme::imex_22:\n        [[fallthrough]];\n      case TimeSteppingScheme::imex_33:\n        [[fallthrough]];\n      case TimeSteppingScheme::strang_ssprk_33_cn:\n        [[fallthrough]];\n      case TimeSteppingScheme::strang_erk_33_cn:\n        [[fallthrough]];\n      case TimeSteppingScheme::strang_erk_43_cn: {\n        AssertThrow(\n            !ParabolicSystem::is_identity,\n            dealii::ExcMessage(\n                \"The selected equation has a trivial parabolic subsystem and \"\n                \"should not be run with an IMEX timestepping scheme.\"));\n        break;\n      }\n      }\n    };\n\n    check_whether_timestepping_makes_sense();\n    this->parse_parameters_call_back.connect(\n        check_whether_timestepping_makes_sense);\n  }\n\n\n  /*\n   * -------------------------------------------------------------------------\n   * Prepare state vector:\n   * -------------------------------------------------------------------------\n   */\n\n\n  template <typename Description, int dim, typename Number>\n  void TimeIntegrator<Description, dim, Number>::prepare_state_vector(\n      StateVector &state_vector, Number t) const\n  {\n    if (!ParabolicSystem::is_identity)\n      parabolic_module_->prepare_state_vector(state_vector, t);\n    hyperbolic_module_->prepare_state_vector(state_vector, t);\n  }\n\n\n  /*\n   * -------------------------------------------------------------------------\n   * High level step function implementing various CFLRecoveryStrategy\n   * -------------------------------------------------------------------------\n   */\n\n\n  template <typename Description, int dim, typename Number>\n  Number TimeIntegrator<Description, dim, Number>::step(\n      StateVector &state_vector,\n      Number t,\n      Number t_final /*=std::numeric_limits<Number>::max()*/)\n  {\n    Number tau_max =\n        std::min(tau_max_, t_final - t); /* enforces t <= t_final */\n\n#ifdef DEBUG_OUTPUT\n    std::cout << \"TimeIntegrator<dim, Number>::step()\" << std::endl;\n    std::cout << \"        enforcing tau_max <= \" << tau_max << std::endl;\n#endif\n\n    const auto single_step = [&]() {\n      switch (time_stepping_scheme_) {\n      case TimeSteppingScheme::ssprk_22:\n        return step_ssprk_22(state_vector, t, tau_max);\n      case TimeSteppingScheme::ssprk_33:\n        return step_ssprk_33(state_vector, t, tau_max);\n      case TimeSteppingScheme::erk_11:\n        return step_erk_11(state_vector, t, tau_max);\n      case TimeSteppingScheme::erk_22:\n        return step_erk_22(state_vector, t, tau_max);\n      case TimeSteppingScheme::erk_33:\n        return step_erk_33(state_vector, t, tau_max);\n      case TimeSteppingScheme::erk_43:\n        return step_erk_43(state_vector, t, tau_max);\n      case TimeSteppingScheme::erk_54:\n        return step_erk_54(state_vector, t, tau_max);\n      case TimeSteppingScheme::strang_ssprk_33_cn:\n        return step_strang_ssprk_33_cn(state_vector, t, tau_max);\n      case TimeSteppingScheme::strang_erk_33_cn:\n        return step_strang_erk_33_cn(state_vector, t, tau_max);\n      case TimeSteppingScheme::strang_erk_43_cn:\n        return step_strang_erk_43_cn(state_vector, t, tau_max);\n      case TimeSteppingScheme::imex_11:\n        return step_imex_11(state_vector, t, tau_max);\n      case TimeSteppingScheme::imex_22:\n        return step_imex_22(state_vector, t, tau_max);\n      case TimeSteppingScheme::imex_33:\n        return step_imex_33(state_vector, t, tau_max);\n      default:\n        __builtin_unreachable();\n      }\n    };\n\n    if (cfl_recovery_strategy_ != CFLRecoveryStrategy::none) {\n      hyperbolic_module_->set_id_violation_strategy(\n          IDViolationStrategy::raise_exception);\n      parabolic_module_->set_id_violation_strategy(\n          IDViolationStrategy::raise_exception);\n      hyperbolic_module_->set_cfl(cfl_max_);\n    }\n\n    try {\n      return single_step();\n\n    } catch (const Restart &restart) {\n\n      AssertThrow(cfl_recovery_strategy_ != CFLRecoveryStrategy::none,\n                  dealii::ExcInternalError());\n\n      hyperbolic_module_->set_id_violation_strategy(IDViolationStrategy::warn);\n      parabolic_module_->set_id_violation_strategy(IDViolationStrategy::warn);\n\n      if (cfl_recovery_strategy_ == CFLRecoveryStrategy::bang_bang_control) {\n        /* Retry with cfl_min instead of cfl_max: */\n#ifdef DEBUG_OUTPUT\n        std::cout\n            << \"        restart with bang bang control: setting cfl to cfl_min\"\n            << std::endl;\n#endif\n        hyperbolic_module_->set_cfl(cfl_min_);\n      }\n\n      if (cfl_recovery_strategy_ == CFLRecoveryStrategy::cruise_control) {\n        /* Retry with the suggested tau_max: */\n#ifdef DEBUG_OUTPUT\n        std::cout\n            << \"        restart with cruise control: using suggested_tau_max\"\n            << std::endl;\n#endif\n        //\n        // Multiply the suggested tau_max value with the efficiency.\n        //\n        // We have to account for the fact that the e Restart exception is\n        // thrown within a substep of the hyperbolic or parabolic module.\n        // This implies that the suggested_tau_max is computed for that\n        // particular substep and not for the full combined method (where\n        // tau_max can be larger). We thus multiply tau_max with the\n        // efficiency factor.\n        //\n        tau_max =\n            std::min(tau_max, efficiency_ * Number(restart.suggested_tau_max));\n      }\n\n      return single_step();\n    }\n  }\n\n\n  /*\n   * -------------------------------------------------------------------------\n   * Concrete implementation of ERK / IMEX time stepping strategies.\n   * -------------------------------------------------------------------------\n   */\n\n\n  template <typename Description, int dim, typename Number>\n  Number TimeIntegrator<Description, dim, Number>::step_ssprk_22(\n      StateVector &state_vector, Number t, Number tau_max)\n  {\n    /* SSP-RK2, see @cite Shu1988, Eq. 2.15. */\n\n#ifdef DEBUG_OUTPUT\n    std::cout << \"TimeIntegrator<dim, Number>::step_ssprk_22()\" << std::endl;\n#endif\n\n    Assert(efficiency_ == 1., dealii::ExcInternalError());\n\n    /* Step 1: T0 = U_old + tau * L(U_old) at t -> t + tau */\n    Number tau = hyperbolic_module_->template step<0>(\n        state_vector, {}, {}, temp_[0], Number(0.), tau_max);\n\n    /* Step 2: T1 = T0 + tau L(T0) at time t + tau -> t + 2*tau */\n    hyperbolic_module_->prepare_state_vector(temp_[0], t + 1.0 * tau);\n    hyperbolic_module_->template step<0>(temp_[0], {}, {}, temp_[1], tau);\n\n    /* Step 2: convex combination: T1 = 1/2 U_old + 1/2 T1 at time t + tau */\n    sadd(temp_[1], Number(1.0 / 2.0), Number(1.0 / 2.0), state_vector);\n\n    state_vector.swap(temp_[1]);\n    return tau;\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  Number TimeIntegrator<Description, dim, Number>::step_ssprk_33(\n      StateVector &state_vector, Number t, Number tau_max)\n  {\n    /* SSP-RK3, see @cite Shu1988, Eq. 2.18. */\n\n#ifdef DEBUG_OUTPUT\n    std::cout << \"TimeIntegrator<dim, Number>::step_ssprk_33()\" << std::endl;\n#endif\n\n    Assert(efficiency_ == 1., dealii::ExcInternalError());\n\n    /* Step 1: T0 = U_old + tau * L(U_old) at time t -> t + tau */\n    Number tau = hyperbolic_module_->template step<0>(\n        state_vector, {}, {}, temp_[0], Number(0.), tau_max);\n\n    /* Step 2: T1 = T0 + tau L(T0) at time t + tau -> t + 2*tau */\n    hyperbolic_module_->prepare_state_vector(temp_[0], t + 1.0 * tau);\n    hyperbolic_module_->template step<0>(temp_[0], {}, {}, temp_[1], tau);\n\n    /* Step 2: convex combination T1 = 3/4 U_old + 1/4 T1 at time t + 0.5*tau */\n    sadd(temp_[1], Number(1.0 / 4.0), Number(3.0 / 4.0), state_vector);\n\n    /* Step 3: T0 = T1 + tau L(T1) at time t + 0.5*tau -> t + 1.5*tau */\n    hyperbolic_module_->prepare_state_vector(temp_[1], t + 0.5 * tau);\n    hyperbolic_module_->template step<0>(temp_[1], {}, {}, temp_[0], tau);\n\n    /* Step 3: convex combination: T0 = 1/3 U_old + 2/3 T0 at time t + tau */\n    sadd(temp_[0], Number(2.0 / 3.0), Number(1.0 / 3.0), state_vector);\n\n    state_vector.swap(temp_[0]);\n    return tau;\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  Number TimeIntegrator<Description, dim, Number>::step_erk_11(\n      StateVector &state_vector, Number /*t*/, Number tau_max)\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"TimeIntegrator<dim, Number>::step_erk_11()\" << std::endl;\n#endif\n\n    Assert(efficiency_ == 1., dealii::ExcInternalError());\n\n    /* Step 1: T0 <- {U_old, 1} at time t -> t + tau */\n    Number tau = hyperbolic_module_->template step<0>(\n        state_vector, {}, {}, temp_[0], Number(0.), tau_max);\n\n    state_vector.swap(temp_[0]);\n    return tau;\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  Number TimeIntegrator<Description, dim, Number>::step_erk_22(\n      StateVector &state_vector, Number t, Number tau_max)\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"TimeIntegrator<dim, Number>::step_erk_22()\" << std::endl;\n#endif\n\n    Assert(efficiency_ == 2., dealii::ExcInternalError());\n\n    /* Step 1: T0 <- {U_old, 1} at time t -> t + tau */\n    Number tau = hyperbolic_module_->template step<0>(\n        state_vector, {}, {}, temp_[0], Number(.0), tau_max / efficiency_);\n\n    /* Step 2: T1 <- {T0, 2} and {U_old, -1} at time t + tau -> t + 2*tau */\n    hyperbolic_module_->prepare_state_vector(temp_[0], t + 1.0 * tau);\n    hyperbolic_module_->template step<1>(\n        temp_[0], {{state_vector}}, {{Number(-1.)}}, temp_[1], tau);\n\n    state_vector.swap(temp_[1]);\n    return efficiency_ * tau;\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  Number TimeIntegrator<Description, dim, Number>::step_erk_33(\n      StateVector &state_vector, Number t, Number tau_max)\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"TimeIntegrator<dim, Number>::step_erk_33()\" << std::endl;\n#endif\n\n    Assert(efficiency_ == 3., dealii::ExcInternalError());\n\n    /* Step 1: T0 <- {U_old, 1} at time t -> t + tau */\n    Number tau = hyperbolic_module_->template step<0>(\n        state_vector, {}, {}, temp_[0], Number(0.), tau_max / efficiency_);\n\n    /* Step 2: T1 <- {T0, 2} and {U_old, -1} at time t + 1*tau -> t + 2*tau */\n    hyperbolic_module_->prepare_state_vector(temp_[0], t + 1.0 * tau);\n    hyperbolic_module_->template step<1>(\n        temp_[0], {{state_vector}}, {{Number(-1.)}}, temp_[1], tau);\n\n    /*\n     * Step 3: T2 <- {T1, 9/4} and {T0, -2} and {U_old, 3/4}\n     * at time t + 2*tau -> t + 3*tau\n     */\n    hyperbolic_module_->prepare_state_vector(temp_[1], t + 2.0 * tau);\n    hyperbolic_module_->template step<2>(temp_[1],\n                                         {{state_vector, temp_[0]}},\n                                         {{Number(0.75), Number(-2.)}},\n                                         temp_[2],\n                                         tau);\n\n    state_vector.swap(temp_[2]);\n    return efficiency_ * tau;\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  Number TimeIntegrator<Description, dim, Number>::step_erk_43(\n      StateVector &state_vector, Number t, Number tau_max)\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"TimeIntegrator<dim, Number>::step_erk_43()\" << std::endl;\n#endif\n\n    Assert(efficiency_ == 4., dealii::ExcInternalError());\n\n    /* Step 1: T0 <- {U_old, 1} at time t -> t + tau */\n    Number tau = hyperbolic_module_->template step<0>(\n        state_vector, {}, {}, temp_[0], Number(0.), tau_max / efficiency_);\n\n    /* Step 2: T1 <- {T0, 2} and {U_old, -1} at time t + 1*tau -> t + 2*tau */\n    hyperbolic_module_->prepare_state_vector(temp_[0], t + 1.0 * tau);\n    hyperbolic_module_->template step<1>(\n        temp_[0], {{state_vector}}, {{Number(-1.)}}, temp_[1], tau);\n\n    /* Step 3: T2 <- {T1, 2} and {T0, -1} at time t + 2*tau -> t + 3*tau */\n    hyperbolic_module_->prepare_state_vector(temp_[1], t + 2.0 * tau);\n    hyperbolic_module_->template step<1>(\n        temp_[1], {{temp_[0]}}, {{Number(-1.)}}, temp_[2], tau);\n\n    /*\n     * Step 4: T3 <- {T2, 8/3} and {T1,-10/3} and {T0, 5/3}\n     * at time t + 3*tau -> t + 4*tau\n     */\n    hyperbolic_module_->prepare_state_vector(temp_[2], t + 3.0 * tau);\n    hyperbolic_module_->template step<2>(temp_[2],\n                                         {{temp_[0], temp_[1]}},\n                                         {{Number(5. / 3.), Number(-10. / 3.)}},\n                                         temp_[3],\n                                         tau);\n\n    state_vector.swap(temp_[3]);\n    return efficiency_ * tau;\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  Number TimeIntegrator<Description, dim, Number>::step_erk_54(\n      StateVector &state_vector, Number t, Number tau_max)\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"TimeIntegrator<dim, Number>::step_erk_54()\" << std::endl;\n#endif\n\n    Assert(efficiency_ == 5., dealii::ExcInternalError());\n\n    constexpr Number c = 0.2; /* equidistant c_i */\n    constexpr Number a_21 = +0.2;\n    constexpr Number a_31 = +0.26075582269554909;\n    constexpr Number a_32 = +0.13924417730445096;\n    constexpr Number a_41 = -0.25856517872570289;\n    constexpr Number a_42 = +0.91136274166280729;\n    constexpr Number a_43 = -0.05279756293710430;\n    constexpr Number a_51 = +0.21623276431503774;\n    constexpr Number a_52 = +0.51534223099602405;\n    constexpr Number a_53 = -0.81662794199265554;\n    constexpr Number a_54 = +0.88505294668159373;\n    constexpr Number a_61 = -0.10511678454691901;                  /* aka b_1 */\n    constexpr Number a_62 = +0.87880047152100838;                  /* aka b_2 */\n    constexpr Number a_63 = -0.58903404061484477;                  /* aka b_3 */\n    constexpr Number a_64 = +0.46213380485434047;                  /* aka b_4 */\n    constexpr Number a_65 [[maybe_unused]] = +0.35321654878641495; /* aka b_5 */\n\n    /* Step 1: at time t -> t + 1*tau */\n    Number tau = hyperbolic_module_->template step<0>(\n        state_vector, {}, {}, temp_[0], Number(0.), tau_max / efficiency_);\n\n    /* Step 2: at time t + 1*tau -> t + 2*tau */\n    hyperbolic_module_->prepare_state_vector(temp_[0], t + 1.0 * tau);\n    hyperbolic_module_->template step<1>(\n        temp_[0], {{state_vector}}, {{(a_31 - a_21) / c}}, temp_[1], tau);\n\n    /* Step 3: at time t + 2*tau -> t + 3*tau */\n    hyperbolic_module_->prepare_state_vector(temp_[1], t + 2.0 * tau);\n    hyperbolic_module_->template step<2>(\n        temp_[1],\n        {{state_vector, temp_[0]}},\n        {{(a_41 - a_31) / c, (a_42 - a_32) / c}},\n        temp_[2],\n        tau);\n\n    /* Step 4: at time t + 3*tau -> t + 4*tau */\n    hyperbolic_module_->prepare_state_vector(temp_[2], t + 3.0 * tau);\n    hyperbolic_module_->template step<3>(\n        temp_[2],\n        {{state_vector, temp_[0], temp_[1]}},\n        {{(a_51 - a_41) / c, (a_52 - a_42) / c, (a_53 - a_43) / c}},\n        temp_[3],\n        tau);\n\n    /* Step 5: at time t + 4*tau -> t + 5*tau */\n    hyperbolic_module_->prepare_state_vector(temp_[3], t + 4.0 * tau);\n    hyperbolic_module_->template step<4>(\n        temp_[3],\n        {{state_vector, temp_[0], temp_[1], temp_[2]}},\n        {{(a_61 - a_51) / c,\n          (a_62 - a_52) / c,\n          (a_63 - a_53) / c,\n          (a_64 - a_54) / c}},\n        temp_[4],\n        tau);\n\n    state_vector.swap(temp_[4]);\n    return efficiency_ * tau;\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  Number TimeIntegrator<Description, dim, Number>::step_strang_ssprk_33_cn(\n      StateVector &state_vector, Number t, Number tau_max)\n  {\n    // FIXME: avoid code duplication with step_ssprk_33\n\n#ifdef DEBUG_OUTPUT\n    std::cout << \"TimeIntegrator<dim, Number>::step_strang_ssprk_33_cn()\"\n              << std::endl;\n#endif\n\n    Assert(efficiency_ == 2., dealii::ExcInternalError());\n\n    /* First explicit SSPRK 3 step with final result in temp_[0]: */\n\n    Number tau = hyperbolic_module_->template step<0>(\n        state_vector, {}, {}, temp_[0], Number(0.0), tau_max / efficiency_);\n\n    hyperbolic_module_->prepare_state_vector(temp_[0], t + 1.0 * tau);\n    hyperbolic_module_->template step<0>(temp_[0], {}, {}, temp_[1], tau);\n    sadd(temp_[1], Number(1.0 / 4.0), Number(3.0 / 4.0), /*!*/ state_vector);\n\n    hyperbolic_module_->prepare_state_vector(temp_[1], t + 0.5 * tau);\n    hyperbolic_module_->template step<0>(temp_[1], {}, {}, temp_[0], tau);\n    sadd(temp_[0], Number(2.0 / 3.0), Number(1.0 / 3.0), /*!*/ state_vector);\n\n    /* Implicit Crank-Nicolson step with final result in temp_[2]: */\n\n    try {\n      parabolic_module_->crank_nicolson_step(temp_[0], t, temp_[2], 2.0 * tau);\n    } catch (Restart &restart) {\n      /* Adjust suggested_tau_max. We multiply with efficiency_ again later */\n      restart.suggested_tau_max /= efficiency_;\n      throw;\n    }\n\n    /* Second SSPRK 3 step with final result in temp_[0]: */\n\n    hyperbolic_module_->prepare_state_vector(/*!*/ temp_[2], t + 1.0 * tau);\n    hyperbolic_module_->template step<0>(/*!*/ temp_[2], {}, {}, temp_[0], tau);\n\n    hyperbolic_module_->prepare_state_vector(temp_[0], t + 2.0 * tau);\n    hyperbolic_module_->template step<0>(temp_[0], {}, {}, temp_[1], tau);\n    sadd(temp_[1], Number(1.0 / 4.0), Number(3.0 / 4.0), /*!*/ temp_[2]);\n\n    hyperbolic_module_->prepare_state_vector(temp_[1], t + 1.5 * tau);\n    hyperbolic_module_->template step<0>(temp_[1], {}, {}, temp_[0], tau);\n    sadd(temp_[0], Number(2.0 / 3.0), Number(1.0 / 3.0), /*!*/ temp_[2]);\n\n    state_vector.swap(temp_[0]);\n    return efficiency_ * tau;\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  Number TimeIntegrator<Description, dim, Number>::step_strang_erk_33_cn(\n      StateVector &state_vector, Number t, Number tau_max)\n  {\n    // FIXME: refactor to avoid code duplication with step_erk_33\n\n#ifdef DEBUG_OUTPUT\n    std::cout << \"TimeIntegrator<dim, Number>::step_strang_erk_33_cn()\"\n              << std::endl;\n#endif\n\n    Assert(efficiency_ == 6., dealii::ExcInternalError());\n\n    /* First explicit ERK(3,3,1) step with final result in temp_[2]: */\n\n    Number tau = hyperbolic_module_->template step<0>(\n        state_vector, {}, {}, temp_[0], Number(0.), tau_max / efficiency_);\n\n    hyperbolic_module_->prepare_state_vector(temp_[0], t + 1.0 * tau);\n    hyperbolic_module_->template step<1>(\n        temp_[0], {{state_vector}}, {{Number(-1.)}}, temp_[1], tau);\n\n    hyperbolic_module_->prepare_state_vector(temp_[1], t + 2.0 * tau);\n    hyperbolic_module_->template step<2>(temp_[1],\n                                         {{state_vector, temp_[0]}},\n                                         {{Number(0.75), Number(-2.)}},\n                                         temp_[2],\n                                         tau);\n\n    /* Implicit Crank-Nicolson step with final result in temp_[3]: */\n\n    try {\n      parabolic_module_->crank_nicolson_step(temp_[2], t, temp_[3], 6.0 * tau);\n    } catch (Restart &restart) {\n      /* Adjust suggested_tau_max. We multiply with efficiency_ again later */\n      restart.suggested_tau_max /= efficiency_;\n      throw;\n    }\n\n    /* Second explicit ERK(3,3,1) 3 step with final result in temp_[2]: */\n\n    hyperbolic_module_->prepare_state_vector(temp_[3], t + 3.0 * tau);\n    hyperbolic_module_->template step<0>(\n        /*!*/ temp_[3], {}, {}, temp_[0], tau);\n\n    hyperbolic_module_->prepare_state_vector(temp_[0], t + 4.0 * tau);\n    hyperbolic_module_->template step<1>(\n        temp_[0], {{/*!*/ temp_[3]}}, {{Number(-1.)}}, temp_[1], tau);\n\n    hyperbolic_module_->prepare_state_vector(temp_[1], t + 5.0 * tau);\n    hyperbolic_module_->template step<2>(temp_[1],\n                                         {{/*!*/ temp_[3], temp_[0]}},\n                                         {{Number(0.75), Number(-2.)}},\n                                         temp_[2],\n                                         tau);\n\n    state_vector.swap(temp_[2]);\n    return efficiency_ * tau;\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  Number TimeIntegrator<Description, dim, Number>::step_strang_erk_43_cn(\n      StateVector &state_vector, Number t, Number tau_max)\n  {\n    // FIXME: refactor to avoid code duplication with step_erk_43\n\n#ifdef DEBUG_OUTPUT\n    std::cout << \"TimeIntegrator<dim, Number>::step_strang_erk_43_cn()\"\n              << std::endl;\n#endif\n\n    Assert(efficiency_ == 8., dealii::ExcInternalError());\n\n    /* First explicit ERK(4,3,1) step with final result in temp_[3]: */\n\n    Number tau = hyperbolic_module_->template step<0>(\n        state_vector, {}, {}, temp_[0], Number(0.), tau_max / efficiency_);\n\n    hyperbolic_module_->prepare_state_vector(temp_[0], t + 1.0 * tau);\n    hyperbolic_module_->template step<1>(\n        temp_[0], {{state_vector}}, {{Number(-1.)}}, temp_[1], tau);\n\n    hyperbolic_module_->prepare_state_vector(temp_[1], t + 2.0 * tau);\n    hyperbolic_module_->template step<1>(\n        temp_[1], {{temp_[0]}}, {{Number(-1.)}}, temp_[2], tau);\n\n    hyperbolic_module_->prepare_state_vector(temp_[2], t + 3.0 * tau);\n    hyperbolic_module_->template step<2>(temp_[2],\n                                         {{temp_[0], temp_[1]}},\n                                         {{Number(5. / 3.), Number(-10. / 3.)}},\n                                         temp_[3],\n                                         tau);\n\n    /* Implicit Crank-Nicolson step with final result in temp_[2]: */\n\n    try {\n      parabolic_module_->crank_nicolson_step(temp_[3], t, temp_[2], 8.0 * tau);\n    } catch (Restart &restart) {\n      /* Adjust suggested_tau_max. We multiply with efficiency_ again later */\n      restart.suggested_tau_max /= efficiency_;\n      throw;\n    }\n\n    /* Second explicit ERK(4,3,1) step with final result in temp_[3]: */\n\n    hyperbolic_module_->prepare_state_vector(temp_[2], t + 4.0 * tau);\n    hyperbolic_module_->template step<0>(\n        /*!*/ temp_[2], {}, {}, temp_[0], tau);\n\n    hyperbolic_module_->prepare_state_vector(temp_[0], t + 5.0 * tau);\n    hyperbolic_module_->template step<1>(\n        temp_[0], {{/*!*/ temp_[2]}}, {{Number(-1.)}}, temp_[1], tau);\n\n    hyperbolic_module_->prepare_state_vector(temp_[1], t + 6.0 * tau);\n    hyperbolic_module_->template step<1>(\n        temp_[1], {{temp_[0]}}, {{Number(-1.)}}, temp_[2], tau);\n\n    hyperbolic_module_->prepare_state_vector(temp_[2], t + 7.0 * tau);\n    hyperbolic_module_->template step<2>(temp_[2],\n                                         {{temp_[0], temp_[1]}},\n                                         {{Number(5. / 3.), Number(-10. / 3.)}},\n                                         temp_[3],\n                                         tau);\n\n    state_vector.swap(temp_[3]);\n    return efficiency_ * tau;\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  Number TimeIntegrator<Description, dim, Number>::step_imex_11(\n      StateVector &state_vector, Number t, Number tau_max)\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"TimeIntegrator<dim, Number>::step_imex_11()\" << std::endl;\n#endif\n\n    Assert(efficiency_ == 1., dealii::ExcInternalError());\n\n    /* Explicit step 1: T0 <- {U_old, 1} at time t -> t + tau */\n    Number tau = hyperbolic_module_->template step<0>(\n        state_vector, {}, {}, temp_[0], Number(0.), tau_max);\n\n    /* Implicit step 1: T1 <- {T0, 1} at time t -> t + tau */\n    parabolic_module_->template backward_euler_step<0>(\n        temp_[0], t, {}, {}, temp_[1], 1.0 * tau);\n\n    state_vector.swap(temp_[1]);\n    return tau;\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  Number TimeIntegrator<Description, dim, Number>::step_imex_22(\n      StateVector &state_vector, Number t, Number tau_max)\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"TimeIntegrator<dim, Number>::step_imex_22()\" << std::endl;\n#endif\n\n    Assert(efficiency_ == 2., dealii::ExcInternalError());\n\n    /* Explicit step 1: T0 <- {U_old, 1} at time t -> t + tau */\n    Number tau = hyperbolic_module_->template step<0>(\n        state_vector, {}, {}, temp_[0], Number(0.), tau_max / efficiency_);\n\n    /* Implicit step 1: T1 <- {T0, 1} at time t -> t + tau */\n    parabolic_module_->template backward_euler_step<0>(\n        temp_[0], t, {}, {}, temp_[1], tau);\n\n    /* Explicit step 2: T2 <- {T1, 2} and {U_old, -1} at t + tau -> t + 2 tau */\n    hyperbolic_module_->prepare_state_vector(temp_[1], t + 1.0 * tau);\n    hyperbolic_module_->template step<1>(\n        temp_[1], {{state_vector}}, {{Number(-1.)}}, temp_[2], tau);\n\n    /* Implicit step 2: T3 <- {T2, 0} and {U_old, 1} at t + tau -> t + 2 tau */\n    parabolic_module_->template backward_euler_step<1>(temp_[2],\n                                                       t + 1.0 * tau,\n                                                       {{state_vector}},\n                                                       {{Number(1.)}},\n                                                       temp_[3],\n                                                       tau);\n\n    state_vector.swap(temp_[3]);\n    return efficiency_ * tau;\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  Number TimeIntegrator<Description, dim, Number>::step_imex_33(\n      StateVector &state_vector, Number t, Number tau_max)\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"TimeIntegrator<dim, Number>::step_imex_33()\" << std::endl;\n#endif\n\n    Assert(efficiency_ == 3., dealii::ExcInternalError());\n\n    /* IMEX(3, 3; 1), see @cite ErnGuermond2023, Sec. 4.3. */\n\n    const Number gamma = Number(0.5) + std::sqrt(Number(3.0)) / Number(6.0);\n\n    /* Explicit step 1: T0 <- {U_old, 1} at time t -> t + tau */\n    Number tau = hyperbolic_module_->template step<0>(\n        state_vector, {}, {}, temp_[0], Number(0.), tau_max / efficiency_);\n\n    /* Implicit step 1: T1 <- {U_old, 1 - 3*gamma} at time t -> t + tau */\n    parabolic_module_->template backward_euler_step<1>(\n        temp_[0],\n        t,\n        {{state_vector}},\n        {{Number(1. - 3. * gamma)}},\n        temp_[1],\n        tau);\n\n    /* Explicit step 2: T2 <- {U_old, -1} and {T1, 2} at time t -> t + 2 tau */\n    hyperbolic_module_->prepare_state_vector(temp_[1], t + 1.0 * tau);\n    hyperbolic_module_->template step<1>(\n        temp_[1], {{state_vector}}, {{Number(-1.)}}, temp_[2], tau);\n\n    /*\n     * Implicit step 2:\n     * T3 <- {U_old, 6*gamma-1} and {T1, 2-9*gamma} at t -> t + * 2 tau\n     */\n    parabolic_module_->template backward_euler_step<2>(\n        temp_[2],\n        t + tau,\n        {{state_vector, temp_[1]}},\n        {{Number(6. * gamma - 1.), Number(2. - 9 * gamma)}},\n        temp_[3],\n        tau);\n\n    /* Explicit step 3: T4 <- {U_old, 3 / 4} and {T1, -2} at t -> t + 3 tau */\n    hyperbolic_module_->prepare_state_vector(temp_[3], t + 2. * tau);\n    hyperbolic_module_->template step<2>(temp_[3],\n                                         {{state_vector, temp_[1]}},\n                                         {{Number(0.75), Number(-2.)}},\n                                         temp_[4],\n                                         tau);\n\n    /* Implicit step 3: */\n    parabolic_module_->template backward_euler_step<3>(\n        temp_[4],\n        t + 2. * tau,\n        {{state_vector, temp_[1], temp_[3]}},\n        {{Number(0.75 - 3. * gamma),\n          Number(6. * gamma - 2.),\n          Number(9. / 4. - 3. * gamma)}},\n        temp_[5],\n        tau);\n\n    state_vector.swap(temp_[5]);\n    return efficiency_ * tau;\n  }\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/time_loop.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2023 by the ryujin authors\n//\n\n#include \"time_loop.template.h\"\n#include <instantiate.h>\n\nnamespace ryujin\n{\n  /* instantiations */\n  template class TimeLoop<Description, 1, NUMBER>;\n  template class TimeLoop<Description, 2, NUMBER>;\n  template class TimeLoop<Description, 3, NUMBER>;\n\n} // namespace ryujin\n"
  },
  {
    "path": "source/time_loop.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"discretization.h\"\n#include \"hyperbolic_module.h\"\n#include \"initial_values.h\"\n#include \"mesh_adaptor.h\"\n#include \"mpi_ensemble.h\"\n#include \"mpi_ensemble_container.h\"\n#include \"offline_data.h\"\n#include \"parabolic_module.h\"\n#include \"postprocessor.h\"\n#include \"quantities.h\"\n#include \"solution_transfer.h\"\n#include \"time_integrator.h\"\n#include \"vtu_output.h\"\n\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/base/timer.h>\n\n#include <fstream>\n\nnamespace ryujin\n{\n  /**\n   * The high-level time loop driving the computation.\n   *\n   * @ingroup TimeLoop\n   */\n  template <typename Description, int dim, typename Number = double>\n  class TimeLoop final : public dealii::ParameterAcceptor\n  {\n  public:\n    /**\n     * @name Typedefs and constexpr constants\n     */\n    //@{\n\n    using HyperbolicSystem = typename Description::HyperbolicSystem;\n\n    using View =\n        typename Description::template HyperbolicSystemView<dim, Number>;\n\n    using ParabolicSystem = typename Description::ParabolicSystem;\n\n    using ScalarNumber = typename View::ScalarNumber;\n\n    static constexpr auto problem_dimension = View::problem_dimension;\n\n    static constexpr auto n_precomputed_values = View::n_precomputed_values;\n\n    using StateVector = typename View::StateVector;\n\n    //@}\n    /**\n     * @name Constructor and setup\n     */\n    //@{\n\n    /**\n     * Constructor.\n     */\n    TimeLoop(const MPI_Comm &mpi_comm);\n\n    /**\n     * Run the high-level time loop.\n     */\n    void run();\n\n  protected:\n    /**\n     * @name Private methods for run()\n     */\n    //@{\n\n    /**\n     * Performs a resume operation. Given a @p base_name the function tries\n     * to locate correponding checkpoint files and will read in the saved\n     * state @p state_vector at saved time @p t with saved output cycle\n     * @p output_cycle.\n     */\n    template <typename Callable>\n    void read_checkpoint(StateVector &state_vector,\n                         const std::string &base_name,\n                         Number &t,\n                         unsigned int &output_cycle,\n                         const Callable &prepare_compute_kernels);\n\n    /**\n     * Write out a checkpoint to disk. Given a @p base_name and a current\n     * state @p U at time @p t and output cycle @p output_cycle the\n     * function writes out the state to disk using boost::archive for\n     * serialization.\n     *\n     * @pre the state_vector has to have been prepared prior to a call to\n     * write_checkpoint().\n     */\n    void write_checkpoint(const StateVector &state_vector,\n                          const std::string &base_name,\n                          const Number &t,\n                          const unsigned int &output_cycle);\n\n    /**\n     * Perform a mesh adaptation cycle according to the selected strategy\n     * in the MeshAdaptor class. The state vector is transferred to the new\n     * discretization.\n     */\n    template <typename Callable>\n    void adapt_mesh_and_transfer_state_vector(\n        StateVector &state_vector, const Callable &prepare_compute_kernels);\n\n    void compute_error(StateVector &state_vector, Number t);\n\n    void output(StateVector &state_vector,\n                const std::string &name,\n                const Number t,\n                const unsigned int cycle);\n\n    void print_parameters(std::ostream &stream);\n    void print_mpi_partition(std::ostream &stream);\n\n    void print_info(const std::string &header);\n\n    void print_head(const std::string &header,\n                    const std::string &secondary,\n                    std::ostream &stream);\n\n    void print_information(unsigned int output_cycle,\n                           Number last_checkpoint,\n                           std::ostream &stream,\n                           bool final_time = false);\n    void print_memory_statistics(std::ostream &stream);\n    void print_timers(std::ostream &stream);\n    void print_throughput(unsigned int cycle,\n                          Number t,\n                          std::ostream &stream,\n                          bool final_time = false);\n\n    void print_cycle_statistics(unsigned int cycle,\n                                Number t,\n                                unsigned int output_cycle,\n                                Number last_checkpoint,\n                                bool write_to_logfile = false,\n                                bool final_time = false);\n    //@}\n\n  private:\n    /**\n     * @name Run time options\n     */\n    //@{\n\n    std::string base_name_;\n    std::string base_name_ensemble_;\n\n    std::string debug_command_;\n    std::string debug_filename_;\n\n    Number t_final_;\n    bool enforce_t_final_;\n    Number timer_granularity_;\n\n    bool enable_output_full_;\n    bool enable_output_levelsets_;\n    bool enable_compute_error_;\n    bool enable_compute_quantities_;\n    bool enable_mesh_adaptivity_;\n\n    unsigned int timer_output_full_multiplier_;\n    unsigned int timer_output_levelsets_multiplier_;\n    unsigned int timer_compute_quantities_multiplier_;\n\n    std::vector<std::string> error_quantities_;\n    bool error_normalize_;\n\n    bool resume_;\n    bool resume_at_time_zero_;\n\n    Number terminal_update_interval_;\n    bool terminal_correct_for_hypertreadhing_;\n\n    Number checkpoint_update_interval_;\n\n    //@}\n    /**\n     * @name Internal data:\n     */\n    //@{\n\n    MPIEnsemble mpi_ensemble_;\n\n    std::map<std::string, dealii::Timer> computing_timer_;\n\n    MPIEnsembleContainer<HyperbolicSystem> hyperbolic_system_;\n    MPIEnsembleContainer<ParabolicSystem> parabolic_system_;\n    Discretization<dim> discretization_;\n    OfflineData<dim, Number> offline_data_;\n    MPIEnsembleContainer<InitialValues<Description, dim, Number>>\n        initial_values_;\n    HyperbolicModule<Description, dim, Number> hyperbolic_module_;\n    ParabolicModule<Description, dim, Number> parabolic_module_;\n    TimeIntegrator<Description, dim, Number> time_integrator_;\n    MeshAdaptor<Description, dim, Number> mesh_adaptor_;\n    SolutionTransfer<Description, dim, Number> solution_transfer_;\n    Postprocessor<Description, dim, Number> postprocessor_;\n    VTUOutput<Description, dim, Number> vtu_output_;\n    Quantities<Description, dim, Number> quantities_;\n\n    dealii::types::global_dof_index n_global_dofs_;\n\n    std::ofstream logfile_; /* log file */\n\n    //@}\n  };\n\n} // namespace ryujin\n"
  },
  {
    "path": "source/time_loop.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include \"mpi_ensemble_container.h\"\n#include \"scope.h\"\n#include \"state_vector.h\"\n#include \"time_loop.h\"\n#include \"version_info.h\"\n\n#include <deal.II/base/logstream.h>\n#include <deal.II/base/work_stream.h>\n#include <deal.II/numerics/vector_tools.h>\n#include <deal.II/numerics/vector_tools.templates.h>\n\n#include <cstdlib>\n#include <filesystem>\n#include <fstream>\n#include <iomanip>\n\n#ifdef WITH_OPENMP\n#include \"omp.h\"\n#endif\n\nusing namespace dealii;\n\nnamespace ryujin\n{\n  template <typename Description, int dim, typename Number>\n  TimeLoop<Description, dim, Number>::TimeLoop(const MPI_Comm &mpi_comm)\n      : ParameterAcceptor(\"/A - TimeLoop\")\n      , mpi_ensemble_(mpi_comm,\n                      [] {\n                        if constexpr (has_n_mpi_ensembles_v<Description>)\n                          return Description::n_mpi_ensembles();\n                        else\n                          return 1;\n                      }())\n      , hyperbolic_system_(mpi_ensemble_, \"/B - Equation\")\n      , parabolic_system_(mpi_ensemble_, \"/B - Equation\")\n      , discretization_(mpi_ensemble_, \"/C - Discretization\")\n      , offline_data_(mpi_ensemble_, discretization_, \"/D - OfflineData\")\n      , initial_values_(mpi_ensemble_,\n                        \"/E - InitialValues\",\n                        mpi_ensemble_,\n                        offline_data_,\n                        hyperbolic_system_,\n                        parabolic_system_)\n      , hyperbolic_module_(mpi_ensemble_,\n                           computing_timer_,\n                           offline_data_,\n                           hyperbolic_system_,\n                           initial_values_,\n                           \"/F - HyperbolicModule\")\n      , parabolic_module_(mpi_ensemble_,\n                          computing_timer_,\n                          offline_data_,\n                          hyperbolic_system_,\n                          parabolic_system_,\n                          initial_values_,\n                          \"/G - ParabolicModule\")\n      , time_integrator_(mpi_ensemble_,\n                         offline_data_,\n                         hyperbolic_module_,\n                         parabolic_module_,\n                         \"/H - TimeIntegrator\")\n      , mesh_adaptor_(mpi_ensemble_,\n                      offline_data_,\n                      hyperbolic_system_,\n                      parabolic_system_,\n                      hyperbolic_module_.initial_precomputed(),\n                      hyperbolic_module_.alpha(),\n                      \"/I - MeshAdaptor\")\n      , solution_transfer_(mpi_ensemble_,\n                           offline_data_,\n                           hyperbolic_system_,\n                           parabolic_system_,\n                           \"/I - MeshAdaptor\")\n      , postprocessor_(mpi_ensemble_,\n                       offline_data_,\n                       hyperbolic_system_,\n                       parabolic_system_,\n                       \"/J - VTUOutput\")\n      , vtu_output_(mpi_ensemble_,\n                    offline_data_,\n                    hyperbolic_system_,\n                    parabolic_system_,\n                    postprocessor_,\n                    hyperbolic_module_.initial_precomputed(),\n                    hyperbolic_module_.alpha(),\n                    mesh_adaptor_.smoothness_indicators(),\n                    \"/J - VTUOutput\")\n      , quantities_(mpi_ensemble_,\n                    offline_data_,\n                    hyperbolic_system_,\n                    parabolic_system_,\n                    \"/K - Quantities\")\n  {\n    base_name_ = \"test\";\n    add_parameter(\"basename\", base_name_, \"Base name for all output files\");\n\n    t_final_ = Number(5.);\n    add_parameter(\"final time\", t_final_, \"Final time\");\n\n    enforce_t_final_ = false;\n    add_parameter(\"enforce final time\",\n                  enforce_t_final_,\n                  \"Boolean indicating whether the final time should be \"\n                  \"enforced strictly. If set to true the last time step is \"\n                  \"shortened so that the simulation ends precisely at t_final\");\n\n    timer_granularity_ = Number(0.01);\n    add_parameter(\"timer granularity\",\n                  timer_granularity_,\n                  \"The timer granularity specifies the time interval after \"\n                  \"which compute, output, postprocessing, and mesh adaptation \"\n                  \"routines are run. This \\\"baseline tick\\\" is further \"\n                  \"modified by the corresponding \\\"*_multiplier\\\" options\");\n\n    enable_output_full_ = false;\n    add_parameter(\"enable output full\",\n                  enable_output_full_,\n                  \"Write out full pvtu records. The frequency is determined by \"\n                  \"\\\"timer granularity\\\" and \\\"timer output full multiplier\\\"\");\n\n    enable_output_levelsets_ = false;\n    add_parameter(\n        \"enable output levelsets\",\n        enable_output_levelsets_,\n        \"Write out levelsets pvtu records. The frequency is determined by \"\n        \"\\\"timer granularity\\\" and \\\"timer output levelsets multiplier\\\"\");\n\n    enable_compute_error_ = false;\n    add_parameter(\"enable compute error\",\n                  enable_compute_error_,\n                  \"Flag to control whether we compute the Linfty Linf_norm of \"\n                  \"the difference to an analytic solution. Implemented only \"\n                  \"for certain initial state configurations.\");\n\n    enable_compute_quantities_ = false;\n    add_parameter(\n        \"enable compute quantities\",\n        enable_compute_quantities_,\n        \"Flag to control whether we compute quantities of interest. The \"\n        \"frequency how often quantities are logged is determined by \\\"timer \"\n        \"granularity\\\" and \\\"timer compute quantities multiplier\\\"\");\n\n    enable_mesh_adaptivity_ = false;\n    add_parameter(\n        \"enable mesh adaptivity\",\n        enable_mesh_adaptivity_,\n        \"Flag to control whether we use an adaptive mesh refinement strategy. \"\n        \"The frequency how often we query MeshAdaptor::analyze() for deciding \"\n        \"on adapting the mesh is determined by \\\"timer granularity\\\" and \"\n        \"\\\"timer mesh refinement multiplier\\\"\");\n\n    timer_output_full_multiplier_ = 1;\n    add_parameter(\"timer output full multiplier\",\n                  timer_output_full_multiplier_,\n                  \"Multiplicative modifier applied to \\\"timer granularity\\\" \"\n                  \"that determines the full pvtu writeout granularity\");\n\n    timer_output_levelsets_multiplier_ = 1;\n    add_parameter(\"timer output levelsets multiplier\",\n                  timer_output_levelsets_multiplier_,\n                  \"Multiplicative modifier applied to \\\"timer granularity\\\" \"\n                  \"that determines the levelsets pvtu writeout granularity\");\n\n    timer_compute_quantities_multiplier_ = 1;\n    add_parameter(\n        \"timer compute quantities multiplier\",\n        timer_compute_quantities_multiplier_,\n        \"Multiplicative modifier applied to \\\"timer granularity\\\" that \"\n        \"determines the writeout granularity for quantities of interest\");\n\n    std::copy(std::begin(View::component_names),\n              std::end(View::component_names),\n              std::back_inserter(error_quantities_));\n\n    add_parameter(\"error quantities\",\n                  error_quantities_,\n                  \"List of conserved quantities used in the computation of the \"\n                  \"error norms.\");\n\n    error_normalize_ = true;\n    add_parameter(\"error normalize\",\n                  error_normalize_,\n                  \"Flag to control whether the error should be normalized by \"\n                  \"the corresponding norm of the analytic solution.\");\n\n    resume_ = false;\n    add_parameter(\"resume\", resume_, \"Resume an interrupted computation\");\n\n    resume_at_time_zero_ = false;\n    add_parameter(\"resume at time zero\",\n                  resume_at_time_zero_,\n                  \"Resume from the latest checkpoint but set the time to t=0.\");\n\n    terminal_update_interval_ = 5;\n    add_parameter(\"terminal update interval\",\n                  terminal_update_interval_,\n                  \"Number of seconds after which output statistics are \"\n                  \"recomputed and printed on the terminal. Setting the \"\n                  \"interval to zero disables terminal output.\");\n\n    terminal_correct_for_hypertreadhing_ = true;\n    add_parameter(\n        \"terminal correct for hyperthreading\",\n        terminal_correct_for_hypertreadhing_,\n        \"If set to true, the CPU throughput is corrected by dividing the total \"\n        \"consumed CPU time by a factor of 2. This correction is only active if \"\n        \"the number of threads (per MPI rank) is 2.\");\n\n    checkpoint_update_interval_ = 0;\n    add_parameter(\n        \"checkpoint update interval\",\n        checkpoint_update_interval_,\n        \"Number of seconds after which a new checkpoint is written out to \"\n        \"disk. Setting the interval to zero disables checkpointing.\");\n\n    debug_command_ = \"\";\n    add_parameter(\"debug command\",\n                  debug_command_,\n                  \"If set to a nonempty string then the host environment's \"\n                  \"command processor is invoked via std::system() with the \"\n                  \"specified string as command parameter.\");\n\n    debug_filename_ = \"\";\n    add_parameter(\"debug filename\",\n                  debug_filename_,\n                  \"If set to a nonempty string then we output the contents of \"\n                  \"this file at the end. This is mainly useful in the \"\n                  \"testsuite to output files we wish to compare\");\n  }\n\n\n  /*\n   * ---------------------------------------------------------------------------\n   * Setup and main loop:\n   * ---------------------------------------------------------------------------\n   */\n\n\n  template <typename Description, int dim, typename Number>\n  void TimeLoop<Description, dim, Number>::run()\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"TimeLoop<dim, Number>::run()\" << std::endl;\n#endif\n\n    {\n      base_name_ensemble_ = base_name_;\n      if (mpi_ensemble_.n_ensembles() > 1) {\n        print_info(\"setting up MPI ensemble\");\n        unsigned int digits =\n            dealii::Utilities::needed_digits(mpi_ensemble_.n_ensembles() - 1);\n        base_name_ensemble_ +=\n            \"-ensemble_\" +\n            dealii::Utilities::int_to_string(mpi_ensemble_.ensemble(), digits);\n      }\n    }\n\n    /* Attach log file and record runtime parameters: */\n\n    if (mpi_ensemble_.world_rank() == 0)\n      logfile_.open(base_name_ + \".log\");\n\n    print_parameters(logfile_);\n\n    /*\n     * Prepare data structures:\n     */\n\n    Number t = 0.;\n    unsigned int timer_cycle = 0;\n    StateVector state_vector;\n\n    /* Create a small lambda for preparing compute kernels: */\n    const auto prepare_compute_kernels = [&]() {\n      print_info(\"preparing compute kernels\");\n\n      offline_data_.prepare(problem_dimension, n_precomputed_values);\n\n      hyperbolic_module_.prepare();\n      parabolic_module_.prepare();\n      time_integrator_.prepare();\n      mesh_adaptor_.prepare(/*needs current timepoint*/ t);\n      postprocessor_.prepare();\n      vtu_output_.prepare();\n      quantities_.prepare(base_name_ensemble_);\n      print_mpi_partition(logfile_);\n\n      if (mpi_ensemble_.ensemble_rank() == 0)\n        n_global_dofs_ = dealii::Utilities::MPI::sum(\n            offline_data_.dof_handler().n_dofs(),\n            mpi_ensemble_.ensemble_leader_communicator());\n    };\n\n    {\n      Scope scope(computing_timer_, \"(re)initialize data structures\");\n      print_info(\"initializing data structures\");\n\n      if (resume_) {\n        print_info(\"resume: reading mesh and loading state vector\");\n\n        read_checkpoint(state_vector,\n                        base_name_ensemble_,\n                        t,\n                        timer_cycle,\n                        prepare_compute_kernels);\n\n        if (resume_at_time_zero_) {\n          /* Reset the current time t and the output cycle count to zero: */\n          t = 0.;\n          timer_cycle = 0;\n        }\n\n      } else {\n        print_info(\"creating mesh and interpolating initial values\");\n\n        discretization_.prepare(base_name_ensemble_);\n\n        prepare_compute_kernels();\n\n        hyperbolic_module_.reinit_state_vector(state_vector);\n        parabolic_module_.reinit_state_vector(state_vector);\n        {\n          Scope scope(computing_timer_,\n                      \"time step [X]   - interpolate data vectors\");\n          std::get<0>(state_vector) =\n              initial_values_.get().interpolate_hyperbolic_vector();\n        }\n        Vectors::debug_poison_invalid_values(state_vector, offline_data_);\n      }\n    }\n\n    /* Prepare the state vector for time stepping. */\n    time_integrator_.prepare_state_vector(state_vector, t);\n\n    /*\n     * The honorable main loop:\n     */\n\n    Number last_terminal_output = terminal_update_interval_ == Number(0.)\n                                      ? std::numeric_limits<Number>::max()\n                                      : Number(0.);\n    Number last_checkpoint = checkpoint_update_interval_ == Number(0.)\n                                 ? std::numeric_limits<Number>::max()\n                                 : Number(0.);\n\n    print_info(\"entering main loop\");\n    computing_timer_[\"time loop\"].start();\n\n    constexpr Number relax =\n        Number(1.) - Number(10.) * std::numeric_limits<Number>::epsilon();\n\n    unsigned int cycle = 1;\n    for (;; ++cycle) {\n\n#ifdef DEBUG_OUTPUT\n      std::cout << \"\\n\\n###   cycle = \" << cycle << \"   ###\\n\\n\" << std::endl;\n#endif\n\n      /* Accumulate quantities of interest: */\n\n      if (enable_compute_quantities_) {\n        Scope scope(computing_timer_,\n                    \"time step [X]   - accumulate quantities\");\n        quantities_.accumulate(state_vector, t);\n      }\n\n      /* Perform output tasks whenever we reach a timer tick: */\n\n      if (t >= relax * timer_cycle * timer_granularity_) {\n        if (enable_compute_error_) {\n          /*\n           * FIXME: We interpolate the analytic solution at every timer\n           * tick. If we happen to actually not output anything then this\n           * is terribly inefficient...\n           */\n\n          StateVector analytic;\n          {\n            Scope scope(computing_timer_,\n                        \"time step [X]   - interpolate data vectors\");\n            hyperbolic_module_.reinit_state_vector(analytic);\n            parabolic_module_.reinit_state_vector(analytic);\n            std::get<0>(analytic) =\n                initial_values_.get().interpolate_hyperbolic_vector(t);\n          }\n\n          time_integrator_.prepare_state_vector(analytic, t);\n\n          output(analytic,\n                 base_name_ensemble_ + \"-analytic_solution\",\n                 t,\n                 timer_cycle);\n        }\n\n        output(state_vector, base_name_ensemble_ + \"-solution\", t, timer_cycle);\n\n        if (enable_compute_quantities_ &&\n            (timer_cycle % timer_compute_quantities_multiplier_ == 0)) {\n          Scope scope(computing_timer_,\n                      \"time step [X]   - write out quantities\");\n          quantities_.write_out(state_vector, t, timer_cycle);\n        }\n\n        ++timer_cycle;\n      }\n\n      /* Break if we have reached the final time. */\n\n      if (t >= relax * t_final_)\n        break;\n\n      /* Peform a mesh adaptation cycle: */\n\n      if (enable_mesh_adaptivity_) {\n        {\n          Scope scope(computing_timer_,\n                      \"time step [X]   - analyze for mesh adaptation\");\n\n          mesh_adaptor_.analyze(state_vector, t, cycle);\n        }\n\n        if (mesh_adaptor_.need_mesh_adaptation()) {\n          Scope scope_1(computing_timer_, \"(re)initialize data structures\");\n          Scope scope_2(computing_timer_,\n                        \"time step [X]   - perform mesh adaptation\");\n          print_info(\"performing mesh adaptation\");\n\n          adapt_mesh_and_transfer_state_vector(state_vector,\n                                               prepare_compute_kernels);\n\n          /* Prepare the state vector for time stepping. */\n          time_integrator_.prepare_state_vector(state_vector, t);\n        }\n      }\n\n      /* Perform a time step: */\n\n      const auto tau = time_integrator_.step(\n          state_vector,\n          t,\n          enforce_t_final_\n              ? std::min(t_final_, timer_cycle * timer_granularity_)\n              : std::numeric_limits<Number>::max());\n\n      t += tau;\n\n      time_integrator_.prepare_state_vector(state_vector, t);\n\n      /* Synchronize wall time: */\n\n      auto wall_time = computing_timer_[\"time loop\"].wall_time();\n      {\n        Scope scope(computing_timer_,\n                    \"time step [X] _ - synchronization barriers\");\n        wall_time =\n            Utilities::MPI::max(wall_time, mpi_ensemble_.world_communicator());\n      }\n\n      /* Print and record cycle statistics: */\n\n      const bool write_to_log_file =\n          (terminal_update_interval_ != Number(0.)) && /* suppress output */\n          (t >= relax * timer_cycle * timer_granularity_);\n\n      const bool update_terminal =\n          (wall_time >= last_terminal_output + terminal_update_interval_);\n\n      if (write_to_log_file || update_terminal) {\n        Scope scope(computing_timer_,\n                    \"time step [X] _ - synchronization barriers\");\n        print_cycle_statistics(cycle,\n                               t,\n                               timer_cycle,\n                               last_checkpoint,\n                               /*logfile*/ write_to_log_file);\n        last_terminal_output = wall_time;\n      }\n\n      const bool update_checkpoint =\n          (wall_time >= last_checkpoint + checkpoint_update_interval_);\n\n      if (update_checkpoint) {\n        Scope scop(computing_timer_, \"time step [X]   - perform checkpointing\");\n\n        print_info(\"scheduling checkpointing\");\n        write_checkpoint(state_vector, base_name_ensemble_, t, timer_cycle);\n        last_checkpoint = wall_time;\n      }\n    } /* end of loop */\n\n    /* We have actually performed one cycle less. */\n    --cycle;\n\n    if (checkpoint_update_interval_ != Number(0.)) {\n      Scope scope(computing_timer_, \"time step [X]   - perform checkpointing\");\n\n      print_info(\"scheduling checkpointing\");\n      write_checkpoint(state_vector, base_name_ensemble_, t, timer_cycle);\n    }\n\n    computing_timer_[\"time loop\"].stop();\n\n    if (terminal_update_interval_ != Number(0.)) {\n      /* Write final timing statistics to screen and logfile: */\n      print_cycle_statistics(cycle,\n                             t,\n                             timer_cycle,\n                             last_checkpoint,\n                             /*logfile*/ true,\n                             /*final*/ true);\n    }\n\n    if (enable_compute_error_) {\n      /* Output final error: */\n      compute_error(state_vector, t);\n    }\n\n    /*\n     *\n     */\n\n    if (mpi_ensemble_.world_rank() == 0) {\n      if (debug_command_ != \"\") {\n        auto result [[maybe_unused]] = std::system(debug_command_.c_str());\n      }\n\n      if (debug_filename_ != \"\") {\n        std::ifstream f(debug_filename_);\n        if (f.is_open())\n          std::cout << f.rdbuf();\n      }\n    }\n  }\n\n\n  /*\n   * ---------------------------------------------------------------------------\n   * Checkpointing, VTK output, and compute error:\n   * ---------------------------------------------------------------------------\n   */\n\n\n  template <typename Description, int dim, typename Number>\n  template <typename Callable>\n  void TimeLoop<Description, dim, Number>::read_checkpoint(\n      StateVector &state_vector,\n      const std::string &base_name,\n      Number &t,\n      unsigned int &timer_cycle,\n      const Callable &prepare_compute_kernels)\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"TimeLoop<dim, Number>::read_checkpoint()\" << std::endl;\n#endif\n\n    /*\n     * Initialize discretization, read in the mesh, and initialize everything:\n     */\n\n#if DEAL_II_VERSION_GTE(9, 6, 0)\n    discretization_.refinement() = 0; /* do not refine */\n    discretization_.prepare(base_name);\n    discretization_.triangulation().load(base_name + \"-checkpoint.mesh\");\n\n#else\n    AssertThrow(false,\n                dealii::ExcMessage(\"write_checkpoint() is not available with \"\n                                   \"deal.II versions prior to 9.6.0\"));\n#endif\n\n    prepare_compute_kernels();\n\n    /*\n     * Read in and broadcast metadata:\n     */\n\n    std::string name = base_name + \"-checkpoint\";\n\n    unsigned int transfer_handle;\n    if (mpi_ensemble_.ensemble_rank() == 0) {\n      std::string meta = name + \".metadata\";\n\n      std::ifstream file(meta, std::ios::binary);\n      boost::archive::binary_iarchive ia(file);\n      ia >> t >> timer_cycle >> transfer_handle;\n    }\n\n    int ierr;\n    if constexpr (std::is_same_v<Number, double>)\n      ierr = MPI_Bcast(\n          &t, 1, MPI_DOUBLE, 0, mpi_ensemble_.ensemble_communicator());\n    else\n      ierr =\n          MPI_Bcast(&t, 1, MPI_FLOAT, 0, mpi_ensemble_.ensemble_communicator());\n    AssertThrowMPI(ierr);\n\n    ierr = MPI_Bcast(&timer_cycle,\n                     1,\n                     MPI_UNSIGNED,\n                     0,\n                     mpi_ensemble_.ensemble_communicator());\n    AssertThrowMPI(ierr);\n\n    ierr = MPI_Bcast(&transfer_handle,\n                     1,\n                     MPI_UNSIGNED,\n                     0,\n                     mpi_ensemble_.ensemble_communicator());\n    AssertThrowMPI(ierr);\n\n    /* Now read in the state vector: */\n\n    hyperbolic_module_.reinit_state_vector(state_vector);\n    parabolic_module_.reinit_state_vector(state_vector);\n\n    solution_transfer_.set_handle(transfer_handle);\n    solution_transfer_.project(state_vector);\n    solution_transfer_.reset_handle();\n    Vectors::debug_poison_invalid_values(state_vector, offline_data_);\n\n    time_integrator_.prepare_state_vector(state_vector, t);\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void TimeLoop<Description, dim, Number>::write_checkpoint(\n      const StateVector &state_vector,\n      const std::string &base_name,\n      const Number &t,\n      const unsigned int &timer_cycle)\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"TimeLoop<dim, Number>::write_checkpoint()\" << std::endl;\n#endif\n\n    solution_transfer_.prepare_projection(state_vector);\n    const auto transfer_handle = solution_transfer_.get_handle();\n    solution_transfer_.reset_handle();\n\n    std::string name = base_name + \"-checkpoint\";\n\n    if (mpi_ensemble_.ensemble_rank() == 0) {\n      for (const std::string suffix :\n           {\".mesh\", \".mesh_fixed.data\", \".mesh.info\", \".metadata\"})\n        if (std::filesystem::exists(name + suffix))\n          std::filesystem::rename(name + suffix, name + suffix + \"~\");\n    }\n\n#if DEAL_II_VERSION_GTE(9, 6, 0)\n    const auto &triangulation = discretization_.triangulation();\n    triangulation.save(name + \".mesh\");\n\n#else\n    AssertThrow(false,\n                dealii::ExcMessage(\"write_checkpoint() is not available with \"\n                                   \"deal.II versions prior to 9.6.0\"));\n#endif\n\n    /*\n     * Now, write out metadata on rank 0:\n     */\n\n    if (mpi_ensemble_.ensemble_rank() == 0) {\n      std::string meta = name + \".metadata\";\n      std::ofstream file(meta, std::ios::binary | std::ios::trunc);\n      boost::archive::binary_oarchive oa(file);\n      oa << t << timer_cycle << transfer_handle;\n    }\n\n    const int ierr = MPI_Barrier(mpi_ensemble_.ensemble_communicator());\n    AssertThrowMPI(ierr);\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  template <typename Callable>\n  void TimeLoop<Description, dim, Number>::adapt_mesh_and_transfer_state_vector(\n      StateVector &state_vector, const Callable &prepare_compute_kernels)\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"TimeLoop<dim, Number>::adapt_mesh_and_transfer_state_vector()\"\n              << std::endl;\n#endif\n\n    AssertThrow(mpi_ensemble_.n_ensembles() == 1, dealii::ExcNotImplemented());\n\n    /*\n     * Mark cells for coarsening and refinement and set up triangulation:\n     */\n\n    auto &triangulation = discretization_.triangulation();\n    mesh_adaptor_.mark_cells_for_coarsening_and_refinement(triangulation);\n\n    triangulation.prepare_coarsening_and_refinement();\n\n    solution_transfer_.prepare_projection(state_vector);\n\n    /* Execute mesh adaptation and project old state to new state vector: */\n\n    triangulation.execute_coarsening_and_refinement();\n    prepare_compute_kernels();\n\n    hyperbolic_module_.reinit_state_vector(state_vector);\n    parabolic_module_.reinit_state_vector(state_vector);\n\n    solution_transfer_.project(state_vector);\n    solution_transfer_.reset_handle();\n    Vectors::debug_poison_invalid_values(state_vector, offline_data_);\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void\n  TimeLoop<Description, dim, Number>::compute_error(StateVector &state_vector,\n                                                    const Number t)\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"TimeLoop<dim, Number>::compute_error()\" << std::endl;\n#endif\n\n    Vector<Number> difference_per_cell(\n        discretization_.triangulation().n_active_cells());\n\n    Number linf_norm = 0.;\n    Number l1_norm = 0;\n    Number l2_norm = 0;\n\n    const auto analytic_U =\n        initial_values_.get().interpolate_hyperbolic_vector(t);\n    const auto &U = std::get<0>(state_vector);\n\n    using ScalarHostVector = Vectors::ScalarHostVector<Number>;\n    ScalarHostVector analytic_component;\n    ScalarHostVector error_component;\n    analytic_component.reinit(offline_data_.scalar_partitioner());\n    error_component.reinit(offline_data_.scalar_partitioner());\n\n    /* Loop over all selected components: */\n    for (const auto &entry : error_quantities_) {\n      const auto &names = View::component_names;\n      const auto pos = std::find(std::begin(names), std::end(names), entry);\n      if (pos == std::end(names)) {\n        AssertThrow(\n            false,\n            dealii::ExcMessage(\"Unknown component name »\" + entry + \"«\"));\n        __builtin_trap();\n      }\n\n      const auto index = std::distance(std::begin(names), pos);\n\n      analytic_U.extract_component(analytic_component, index);\n\n      /* Compute norms of analytic solution: */\n\n      Number linf_norm_analytic = 0.;\n      Number l1_norm_analytic = 0.;\n      Number l2_norm_analytic = 0.;\n\n      if (error_normalize_) {\n        linf_norm_analytic = analytic_component.linfty_norm();\n\n        VectorTools::integrate_difference(\n            discretization_.mapping(),\n            offline_data_.dof_handler(),\n            analytic_component,\n            Functions::ZeroFunction<dim, Number>(),\n            difference_per_cell,\n            discretization_.quadrature_high_order(),\n            VectorTools::L1_norm);\n\n        l1_norm_analytic =\n            Utilities::MPI::sum(difference_per_cell.l1_norm(),\n                                mpi_ensemble_.ensemble_communicator());\n\n        VectorTools::integrate_difference(\n            discretization_.mapping(),\n            offline_data_.dof_handler(),\n            analytic_component,\n            Functions::ZeroFunction<dim, Number>(),\n            difference_per_cell,\n            discretization_.quadrature_high_order(),\n            VectorTools::L2_norm);\n\n        l2_norm_analytic = Number(std::sqrt(\n            Utilities::MPI::sum(std::pow(difference_per_cell.l2_norm(), 2),\n                                mpi_ensemble_.ensemble_communicator())));\n      }\n\n      /* Compute norms of error: */\n\n      U.extract_component(error_component, index);\n      /* Populate constrained dofs due to periodicity: */\n      offline_data_.affine_constraints().distribute(error_component);\n      error_component.update_ghost_values();\n      error_component -= analytic_component;\n\n      const Number linf_norm_error = error_component.linfty_norm();\n\n      VectorTools::integrate_difference(discretization_.mapping(),\n                                        offline_data_.dof_handler(),\n                                        error_component,\n                                        Functions::ZeroFunction<dim, Number>(),\n                                        difference_per_cell,\n                                        discretization_.quadrature_high_order(),\n                                        VectorTools::L1_norm);\n\n      const Number l1_norm_error = Utilities::MPI::sum(\n          difference_per_cell.l1_norm(), mpi_ensemble_.ensemble_communicator());\n\n      VectorTools::integrate_difference(discretization_.mapping(),\n                                        offline_data_.dof_handler(),\n                                        error_component,\n                                        Functions::ZeroFunction<dim, Number>(),\n                                        difference_per_cell,\n                                        discretization_.quadrature_high_order(),\n                                        VectorTools::L2_norm);\n\n      const Number l2_norm_error = Number(std::sqrt(\n          Utilities::MPI::sum(std::pow(difference_per_cell.l2_norm(), 2),\n                              mpi_ensemble_.ensemble_communicator())));\n\n      if (error_normalize_) {\n        linf_norm += linf_norm_error / linf_norm_analytic;\n        l1_norm += l1_norm_error / l1_norm_analytic;\n        l2_norm += l2_norm_error / l2_norm_analytic;\n      } else {\n        linf_norm += linf_norm_error;\n        l1_norm += l1_norm_error;\n        l2_norm += l2_norm_error;\n      }\n    }\n\n    if (mpi_ensemble_.ensemble_rank() != 0)\n      return;\n\n    /*\n     * Sum up over all participating MPI ranks. Note: we only perform this\n     * operation on \"peer\" ranks zero:\n     */\n\n    if (mpi_ensemble_.n_ensembles() > 1) {\n      linf_norm = Utilities::MPI::sum(\n          linf_norm, mpi_ensemble_.ensemble_leader_communicator());\n      l1_norm = Utilities::MPI::sum(\n          l1_norm, mpi_ensemble_.ensemble_leader_communicator());\n      l2_norm = Utilities::MPI::sum(\n          l2_norm, mpi_ensemble_.ensemble_leader_communicator());\n    }\n\n    if (mpi_ensemble_.world_rank() != 0)\n      return;\n\n    logfile_ << std::endl << \"Computed errors:\" << std::endl << std::endl;\n    logfile_ << std::setprecision(16);\n\n    std::string description =\n        error_normalize_ ? \"Normalized consolidated\" : \"Consolidated\";\n\n    logfile_ << description + \" Linf, L1, and L2 errors at final time \\n\";\n    logfile_ << std::setprecision(16);\n    logfile_ << \"#dofs = \" << n_global_dofs_ << std::endl;\n    logfile_ << \"t     = \" << t << std::endl;\n    logfile_ << \"Linf  = \" << linf_norm << std::endl;\n    logfile_ << \"L1    = \" << l1_norm << std::endl;\n    logfile_ << \"L2    = \" << l2_norm << std::endl;\n\n    std::cout << description + \" Linf, L1, and L2 errors at final time \\n\";\n    std::cout << std::setprecision(16);\n    std::cout << \"#dofs = \" << n_global_dofs_ << std::endl;\n    std::cout << \"t     = \" << t << std::endl;\n    std::cout << \"Linf  = \" << linf_norm << std::endl;\n    std::cout << \"L1    = \" << l1_norm << std::endl;\n    std::cout << \"L2    = \" << l2_norm << std::endl;\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void TimeLoop<Description, dim, Number>::output(StateVector &state_vector,\n                                                  const std::string &name,\n                                                  const Number t,\n                                                  const unsigned int cycle)\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"TimeLoop<dim, Number>::output(t = \" << t << \")\" << std::endl;\n#endif\n\n    const bool do_full_output =\n        (cycle % timer_output_full_multiplier_ == 0) && enable_output_full_;\n    const bool do_levelsets =\n        (cycle % timer_output_levelsets_multiplier_ == 0) &&\n        enable_output_levelsets_;\n\n    /* There is nothing to do: */\n    if (!(do_full_output || do_levelsets))\n      return;\n\n    /* Data output: */\n\n    Scope scope(computing_timer_, \"time step [X]   - perform vtu output\");\n    print_info(\"scheduling output\");\n\n    postprocessor_.compute(state_vector);\n    /*\n     * Workaround: Manually reset bounds during the first output cycle\n     * (which is often just a uniform flow field) to obtain a better\n     * normailization:\n     */\n    if (cycle == 0)\n      postprocessor_.reset_bounds();\n\n    /* Make sure we have a valid vector of smoothness indicators. */\n    mesh_adaptor_.compute_smoothness_indicators(state_vector);\n\n    vtu_output_.schedule_output(\n        state_vector, name, t, cycle, do_full_output, do_levelsets);\n  }\n\n\n  /*\n   * ---------------------------------------------------------------------------\n   * Output and logging related functions:\n   * ---------------------------------------------------------------------------\n   */\n\n\n  template <typename Description, int dim, typename Number>\n  void\n  TimeLoop<Description, dim, Number>::print_parameters(std::ostream &stream)\n  {\n    if (mpi_ensemble_.world_rank() != 0)\n      return;\n\n    /* Output commit and library information: */\n\n    print_revision_and_version(stream);\n\n    /* Print run time parameters: */\n\n    stream << std::endl << \"Run time parameters:\" << std::endl << std::endl;\n    ParameterAcceptor::prm.print_parameters(\n        stream, ParameterHandler::OutputStyle::ShortPRM);\n    stream << std::endl;\n\n    /* Also print out parameters to a prm file: */\n\n    std::ofstream output(base_name_ + \"-parameters.prm\");\n    ParameterAcceptor::prm.print_parameters(output, ParameterHandler::ShortPRM);\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void\n  TimeLoop<Description, dim, Number>::print_mpi_partition(std::ostream &stream)\n  {\n    /*\n     * Fixme: this conversion to double is really not elegant. We should\n     * improve the Utilities::MPI::min_max_avg function in deal.II to\n     * handle different data types\n     */\n\n    // NOLINTBEGIN\n    std::vector<double> values = {\n        (double)offline_data_.n_export_indices(),\n        (double)offline_data_.n_locally_internal(),\n        (double)offline_data_.n_locally_owned(),\n        (double)offline_data_.n_locally_relevant(),\n        (double)offline_data_.n_export_indices() /\n            (double)offline_data_.n_locally_relevant(),\n        (double)offline_data_.n_locally_internal() /\n            (double)offline_data_.n_locally_relevant(),\n        (double)offline_data_.n_locally_owned() /\n            (double)offline_data_.n_locally_relevant()};\n    // NOLINTEND\n\n    const auto data =\n        Utilities::MPI::min_max_avg(values, mpi_ensemble_.world_communicator());\n\n    if (mpi_ensemble_.world_rank() != 0)\n      return;\n\n    std::ostringstream output;\n\n    unsigned int n =\n        dealii::Utilities::needed_digits(mpi_ensemble_.n_world_ranks());\n\n    const auto print_snippet = [&output, n](const std::string &name,\n                                            const auto &values) {\n      output << name << \": \";\n      // NOLINTBEGIN\n      output << std::setw(9) << (unsigned int)values.min          //\n             << \" [p\" << std::setw(n) << values.min_index << \"] \" //\n             << std::setw(9) << (unsigned int)values.avg << \" \"   //\n             << std::setw(9) << (unsigned int)values.max          //\n             << \" [p\" << std::setw(n) << values.max_index << \"]\"; //\n      // NOLINTEND\n    };\n\n    const auto print_percentages = [&output, n](const auto &percentages) {\n      output << std::endl << \"                  \";\n      output << \"  (\" << std::setw(3) << std::setprecision(2)\n             << percentages.min * 100 << \"% )\"\n             << \" [p\" << std::setw(n) << percentages.min_index << \"] \"\n             << \"   (\" << std::setw(3) << std::setprecision(2)\n             << percentages.avg * 100 << \"% )\"\n             << \" \"\n             << \"   (\" << std::setw(3) << std::setprecision(2)\n             << percentages.max * 100 << \"% )\"\n             << \" [p\" << std::setw(n) << percentages.max_index << \"]\";\n    };\n\n    output << std::endl << std::endl << \"Partition:   \";\n    print_snippet(\"exp\", data[0]);\n    print_percentages(data[4]);\n\n    output << std::endl << \"             \";\n    print_snippet(\"int\", data[1]);\n    print_percentages(data[5]);\n\n    output << std::endl << \"             \";\n    print_snippet(\"own\", data[2]);\n    print_percentages(data[6]);\n\n    output << std::endl << \"             \";\n    print_snippet(\"rel\", data[3]);\n\n    stream << output.str() << std::endl;\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void TimeLoop<Description, dim, Number>::print_info(const std::string &header)\n  {\n    if (mpi_ensemble_.world_rank() != 0)\n      return;\n\n    std::cout << \"[INFO] \" << header << std::endl;\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void\n  TimeLoop<Description, dim, Number>::print_head(const std::string &header,\n                                                 const std::string &secondary,\n                                                 std::ostream &stream)\n  {\n    if (mpi_ensemble_.world_rank() != 0)\n      return;\n\n    const int header_size = header.size();\n    const auto padded_header =\n        std::string(std::max(0, 34 - header_size) / 2, ' ') + header +\n        std::string(std::max(0, 35 - header_size) / 2, ' ');\n\n    const int secondary_size = secondary.size();\n    const auto padded_secondary =\n        std::string(std::max(0, 34 - secondary_size) / 2, ' ') + secondary +\n        std::string(std::max(0, 35 - secondary_size) / 2, ' ');\n\n    /* clang-format off */\n    stream << \"\\n\";\n    stream << \"    ####################################################\\n\";\n    stream << \"    #########\"     <<  padded_header   <<     \"#########\\n\";\n    stream << \"    #########\"     << padded_secondary <<     \"#########\\n\";\n    stream << \"    ####################################################\\n\";\n    stream << std::endl;\n    /* clang-format on */\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void TimeLoop<Description, dim, Number>::print_information(\n      unsigned int timer_cycle,\n      Number last_checkpoint,\n      std::ostream &stream,\n      bool final_time)\n  {\n    static const std::string vectorization_name = [] {\n      constexpr auto width = VectorizedArray<Number>::size();\n\n      std::string result;\n      if (width == 1)\n        result = \"scalar \";\n      else\n        result = std::to_string(width * 8 * sizeof(Number)) + \"bit packed \";\n\n      if constexpr (std::is_same_v<Number, double>)\n        return result + \"double\";\n      else if constexpr (std::is_same_v<Number, float>)\n        return result + \"float\";\n      else\n        __builtin_trap();\n    }();\n\n    stream << \"Information: (HYP) \" << hyperbolic_system_.get().problem_name;\n    if constexpr (!ParabolicSystem::is_identity) {\n      stream << \"\\n             (PAR) \" << parabolic_system_.get().problem_name;\n    }\n    stream << \"\\n             [\" << base_name_ << \"] \";\n    if (mpi_ensemble_.n_ensembles() > 1) {\n      stream << mpi_ensemble_.n_ensembles() << \" ensembles \";\n    }\n    stream << \"with \" << n_global_dofs_ << \" Qdofs on \"\n           << mpi_ensemble_.n_world_ranks() << \" ranks \"\n#if defined(WITH_OPENMP)\n           << \"/ \" << omp_get_max_threads() << \" threads \"\n#ifndef WITH_DEAL_II_THREADS\n           << \"[serial dealii] \"\n#endif\n#elif defined(WITH_DEAL_II_THREADS)\n           << \"/ \" << MultithreadInfo::n_threads() << \" threads \"\n#endif\n           << \"<\" << vectorization_name << \">\\n\";\n\n    stream << \"             Last output cycle \"                    //\n           << timer_cycle - 1                                      //\n           << \" at t = \" << timer_granularity_ * (timer_cycle - 1) //\n           << \"  [ log \";\n\n    if (enable_output_full_)\n      stream << \"full \";\n    if (enable_output_levelsets_)\n      stream << \"levelsets \";\n    if (enable_compute_quantities_)\n      stream << \"quantities \";\n\n    stream << \"]\\n\";\n\n    if (checkpoint_update_interval_ != Number(0.)) {\n      const auto wall_time =\n          Utilities::MPI::min_max_avg(computing_timer_[\"time loop\"].wall_time(),\n                                      mpi_ensemble_.world_communicator());\n\n      if (final_time) {\n        stream << \"             Last checkpoint at FINAL TIME\\n\";\n      } else {\n        stream << \"             Last checkpoint at wall time \"          //\n               << std::setprecision(2) << std::fixed << last_checkpoint //\n               << \"s  (\" << std::setprecision(0)\n               << std::max(0., wall_time.max - last_checkpoint)\n               << \"s ago, interval \" << checkpoint_update_interval_ << \"s)\\n\";\n      }\n    }\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void TimeLoop<Description, dim, Number>::print_memory_statistics(\n      std::ostream &stream)\n  {\n    Utilities::System::MemoryStats stats;\n    Utilities::System::get_memory_stats(stats);\n\n    Utilities::MPI::MinMaxAvg data = Utilities::MPI::min_max_avg(\n        stats.VmRSS / 1024., mpi_ensemble_.world_communicator());\n\n    if (mpi_ensemble_.world_rank() != 0)\n      return;\n\n    std::ostringstream output;\n\n    unsigned int n =\n        dealii::Utilities::needed_digits(mpi_ensemble_.n_world_ranks());\n\n    output << \"\\nMemory:      [MiB]\"                          //\n           << std::setw(8) << data.min                        //\n           << \" [p\" << std::setw(n) << data.min_index << \"] \" //\n           << std::setw(8) << data.avg << \" \"                 //\n           << std::setw(8) << data.max                        //\n           << \" [p\" << std::setw(n) << data.max_index << \"]\"; //\n\n    stream << output.str() << std::endl;\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void TimeLoop<Description, dim, Number>::print_timers(std::ostream &stream)\n  {\n    std::vector<std::ostringstream> output(computing_timer_.size());\n\n    const auto equalize = [&]() {\n      const auto ptr =\n          std::max_element(output.begin(),\n                           output.end(),\n                           [](const auto &left, const auto &right) {\n                             return left.str().length() < right.str().length();\n                           });\n      const auto length = ptr->str().length();\n      for (auto &it : output)\n        it << std::string(length - it.str().length() + 1, ' ');\n    };\n\n    const auto print_wall_time = [&](auto &timer, auto &stream) {\n      const auto wall_time = Utilities::MPI::min_max_avg(\n          timer.wall_time(), mpi_ensemble_.world_communicator());\n\n      constexpr auto eps = std::numeric_limits<double>::epsilon();\n      /*\n       * Cut off at 99.9% to avoid silly percentages cluttering up the\n       * output.\n       */\n      const auto skew_negative = std::max(\n          100. * (wall_time.min - wall_time.avg) / wall_time.avg - eps, -99.9);\n      const auto skew_positive = std::min(\n          100. * (wall_time.max - wall_time.avg) / wall_time.avg + eps, 99.9);\n\n      stream << std::setprecision(2) << std::fixed << std::setw(9)\n             << wall_time.avg << \"s [sk: \" << std::setprecision(1)\n             << std::setw(5) << std::fixed << skew_negative << \"%/\"\n             << std::setw(4) << std::fixed << skew_positive << \"%]\";\n      unsigned int n =\n          dealii::Utilities::needed_digits(mpi_ensemble_.n_world_ranks());\n      stream << \" [p\" << std::setw(n) << wall_time.min_index << \"/\"\n             << wall_time.max_index << \"]\";\n    };\n\n    const auto cpu_time_statistics =\n        Utilities::MPI::min_max_avg(computing_timer_[\"time loop\"].cpu_time(),\n                                    mpi_ensemble_.world_communicator());\n    const double total_cpu_time = cpu_time_statistics.sum;\n\n    const auto print_cpu_time =\n        [&](auto &timer, auto &stream, bool percentage) {\n          const auto cpu_time = Utilities::MPI::min_max_avg(\n              timer.cpu_time(), mpi_ensemble_.world_communicator());\n\n          stream << std::setprecision(2) << std::fixed << std::setw(12)\n                 << cpu_time.sum << \"s \";\n\n          if (percentage)\n            stream << \"(\" << std::setprecision(1) << std::setw(4)\n                   << 100. * cpu_time.sum / total_cpu_time << \"%)\";\n        };\n\n    auto jt = output.begin();\n    for (auto &it : computing_timer_)\n      *jt++ << \"  \" << it.first;\n    equalize();\n\n    jt = output.begin();\n    for (auto &it : computing_timer_)\n      print_wall_time(it.second, *jt++);\n    equalize();\n\n    jt = output.begin();\n    bool compute_percentages = false;\n    for (auto &it : computing_timer_) {\n      print_cpu_time(it.second, *jt++, compute_percentages);\n      if (it.first.starts_with(\"time loop\"))\n        compute_percentages = true;\n    }\n    equalize();\n\n    if (mpi_ensemble_.world_rank() != 0)\n      return;\n\n    stream << std::endl << \"Timer statistics:\\n\";\n    for (auto &it : output)\n      stream << it.str() << std::endl;\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void TimeLoop<Description, dim, Number>::print_throughput(\n      unsigned int cycle, Number t, std::ostream &stream, bool final_time)\n  {\n    /*\n     * Fixme: The global state kept in this function should be refactored\n     * into its own class object.\n     */\n    static struct Data {\n      unsigned int cycle = 0;\n      double t = 0.;\n      double cpu_time_sum = 0.;\n      double cpu_time_avg = 0.;\n      double cpu_time_min = 0.;\n      double cpu_time_max = 0.;\n      double wall_time = 0.;\n    } previous, current;\n\n    static double time_per_second_exp = 0.;\n\n    /* Update statistics: */\n\n    {\n      previous = current;\n\n      current.cycle = cycle;\n      current.t = t;\n\n      const auto wall_time_statistics =\n          Utilities::MPI::min_max_avg(computing_timer_[\"time loop\"].wall_time(),\n                                      mpi_ensemble_.world_communicator());\n      current.wall_time = wall_time_statistics.max;\n\n      const auto cpu_time_statistics =\n          Utilities::MPI::min_max_avg(computing_timer_[\"time loop\"].cpu_time(),\n                                      mpi_ensemble_.world_communicator());\n      current.cpu_time_sum = cpu_time_statistics.sum;\n      current.cpu_time_avg = cpu_time_statistics.avg;\n      current.cpu_time_min = cpu_time_statistics.min;\n      current.cpu_time_max = cpu_time_statistics.max;\n    }\n\n    if (final_time)\n      previous = Data();\n\n    /* Take averages: */\n\n    double delta_cycles = current.cycle - previous.cycle;\n    const double cycles_per_second =\n        delta_cycles / (current.wall_time - previous.wall_time);\n\n    const auto efficiency = time_integrator_.efficiency();\n    const auto n_dofs = static_cast<double>(n_global_dofs_);\n\n    double wall_m_dofs_per_sec = delta_cycles * n_dofs * efficiency / 1.e6 /\n                                 (current.wall_time - previous.wall_time);\n\n    double cpu_m_dofs_per_sec = delta_cycles * n_dofs * efficiency / 1.e6 /\n                                (current.cpu_time_sum - previous.cpu_time_sum);\n\n    /* Determine whether we fudge the CPU timings: */\n    const bool fudge_cpu_timings = terminal_correct_for_hypertreadhing_ &&\n#if defined(WITH_OPENMP)\n                                   (omp_get_max_threads() == 2);\n#elif defined(WITH_DEAL_II_THREADS)\n                                   (MultithreadInfo::n_threads() == 2);\n#else\n                                   false;\n#endif\n\n    if (fudge_cpu_timings)\n      cpu_m_dofs_per_sec *= 2.;\n\n    double cpu_time_skew = (current.cpu_time_max - current.cpu_time_min - //\n                            previous.cpu_time_max + previous.cpu_time_min) /\n                           delta_cycles;\n    /* avoid printing small negative numbers: */\n    cpu_time_skew = std::max(0., cpu_time_skew);\n\n    const double cpu_time_skew_percentage =\n        cpu_time_skew * delta_cycles /\n        (current.cpu_time_avg - previous.cpu_time_avg);\n\n    const double delta_time =\n        (current.t - previous.t) / (current.cycle - previous.cycle);\n    const double time_per_second =\n        (current.t - previous.t) / (current.wall_time - previous.wall_time);\n\n    /* Print Jean-Luc and Martin metrics: */\n\n    std::ostringstream output;\n\n    /* clang-format off */\n    output << std::endl;\n\n    output << \"Throughput:\\n  \"\n           << (fudge_cpu_timings ? \"CPU*: \" : \"CPU : \")\n           << std::setprecision(4) << std::fixed << cpu_m_dofs_per_sec\n           << \" MQ/s  (\"\n           << std::scientific << 1. / cpu_m_dofs_per_sec * 1.e-6\n           << \" s/Qdof/substep)\" << std::endl;\n\n    output << \"        [cpu time skew: \"\n           << std::setprecision(2) << std::scientific << cpu_time_skew\n           << \"s/cycle (\"\n           << std::setprecision(1) << std::setw(4) << std::setfill(' ') << std::fixed\n           << 100. * cpu_time_skew_percentage\n           << \"%)]\" << std::endl;\n\n    output << \"  WALL: \"\n           << std::setprecision(4) << std::fixed << wall_m_dofs_per_sec\n           << \" MQ/s  (\"\n           << std::scientific << 1. / wall_m_dofs_per_sec * 1.e-6\n           << \" s/Qdof/substep)  (\"\n           << std::setprecision(2) << std::fixed << cycles_per_second\n           << \" cycles/s)\" << std::endl;\n\n    const auto &scheme = time_integrator_.time_stepping_scheme();\n    output << \"        [ \"\n           << Patterns::Tools::Convert<TimeSteppingScheme>::to_string(scheme)\n           << \" with CFL = \"\n           << std::setprecision(2) << std::fixed << hyperbolic_module_.cfl()\n           << \" (\"\n           << std::setprecision(0) << std::fixed << hyperbolic_module_.n_restarts()\n           << \"/\"\n           << std::setprecision(0) << std::fixed << parabolic_module_.n_restarts()\n           << \" rsts) (\"\n           << std::setprecision(0) << std::fixed << hyperbolic_module_.n_warnings()\n           << \"/\"\n           << std::setprecision(0) << std::fixed << parabolic_module_.n_warnings()\n           << \" warn) (\"\n           << std::setprecision(0) << std::fixed << hyperbolic_module_.n_corrections()\n           << \"/\"\n           << std::setprecision(0) << std::fixed << parabolic_module_.n_corrections()\n           << \" corr) ]\" << std::endl;\n\n    if constexpr (!ParabolicSystem::is_identity)\n      parabolic_module_.print_solver_statistics(output);\n\n    output << \"        [ dt = \"\n           << std::scientific << std::setprecision(2) << delta_time\n           << \" ( \"\n           << time_per_second\n           << \" dt/s) ]\" << std::endl;\n    /* clang-format on */\n\n    /* And print an ETA: */\n\n    time_per_second_exp = 0.8 * time_per_second_exp + 0.2 * time_per_second;\n    auto eta = static_cast<unsigned int>(std::max(t_final_ - t, Number(0.)) /\n                                         time_per_second_exp);\n\n    output << \"\\n  ETA : \";\n\n    const unsigned int days = eta / (24 * 3600);\n    if (days > 0) {\n      output << days << \" d  \";\n      eta %= 24 * 3600;\n    }\n\n    const unsigned int hours = eta / 3600;\n    if (hours > 0) {\n      output << hours << \" h  \";\n      eta %= 3600;\n    }\n\n    const unsigned int minutes = eta / 60;\n    output << minutes << \" min\";\n\n    output << \"   (terminal update every \" //\n           << std::setprecision(2) << std::fixed << terminal_update_interval_\n           << \"s)\";\n\n    if (mpi_ensemble_.world_rank() != 0)\n      return;\n\n    stream << output.str() << std::endl;\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void TimeLoop<Description, dim, Number>::print_cycle_statistics(\n      unsigned int cycle,\n      Number t,\n      unsigned int timer_cycle,\n      Number last_checkpoint,\n      bool write_to_logfile,\n      bool final_time)\n  {\n    std::ostringstream output;\n\n    /* Print header: */\n\n    std::ostringstream primary;\n    if (final_time) {\n      primary << \"FINAL  (cycle \" << Utilities::int_to_string(cycle, 6) << \")\";\n    } else {\n      primary << \"Cycle  \" << Utilities::int_to_string(cycle, 6) //\n              << \"  (\" << std::fixed << std::setprecision(1)     //\n              << t / t_final_ * 100 << \"%)\";\n    }\n\n    std::ostringstream secondary;\n    secondary << \"at time t = \" << std::setprecision(8) << std::fixed << t;\n\n    print_head(primary.str(), secondary.str(), output);\n\n    /* Print information and statistics: */\n\n    print_information(timer_cycle, last_checkpoint, output, final_time);\n    print_memory_statistics(output);\n    print_timers(output);\n    print_throughput(cycle, t, output, final_time);\n\n    /* Only output on rank 0: */\n    if (mpi_ensemble_.world_rank() != 0)\n      return;\n\n#ifndef DEBUG_OUTPUT\n    std::cout << \"\\033[2J\\033[H\";\n#endif\n    std::cout << output.str() << std::flush;\n\n    if (write_to_logfile) {\n      logfile_ << \"\\n\" << output.str() << std::flush;\n    }\n  }\n} // namespace ryujin\n"
  },
  {
    "path": "source/transfinite_interpolation.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception or LGPL-2.1-or-later\n// Copyright (C) 2007 - 2022 by Martin Kronbichler\n// Copyright (C) 2008 - 2022 by David Wells\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <deal.II/base/config.h>\n#include <deal.II/grid/manifold.h>\n#include <deal.II/grid/tria.h>\n\nnamespace ryujin\n{\n  using namespace dealii; // FIXME: namespace pollution\n\n  /**\n   * This is a copy of the TransfiniteInterpolationManifold shipped with\n   * deal.II. In contrast to the deal.II version it copies the coarse grid\n   * and all relevant Manifold information. That way it can be initialized\n   * with one Triangulation and be used with another Triangulation.\n   *\n   * @ingroup Mesh\n   */\n  template <int dim, int spacedim = dim>\n  class TransfiniteInterpolationManifold : public Manifold<dim, spacedim>\n  {\n  public:\n    TransfiniteInterpolationManifold();\n\n    ~TransfiniteInterpolationManifold() override = default;\n\n    std::unique_ptr<Manifold<dim, spacedim>> clone() const override;\n\n    void initialize(\n        const Triangulation<dim, spacedim> &triangulation,\n        const Manifold<dim, spacedim> &chart_manifold = FlatManifold<dim>());\n\n    Point<spacedim>\n    get_new_point(const ArrayView<const Point<spacedim>> &surrounding_points,\n                  const ArrayView<const double> &weights) const override;\n\n    void\n    get_new_points(const ArrayView<const Point<spacedim>> &surrounding_points,\n                   const Table<2, double> &weights,\n                   ArrayView<Point<spacedim>> new_points) const override;\n\n  private:\n    std::array<unsigned int, 20> get_possible_cells_around_points(\n        const ArrayView<const Point<spacedim>> &surrounding_points) const;\n\n    typename Triangulation<dim, spacedim>::cell_iterator compute_chart_points(\n        const ArrayView<const Point<spacedim>> &surrounding_points,\n        ArrayView<Point<dim>> chart_points) const;\n\n    Point<dim>\n    pull_back(const typename Triangulation<dim, spacedim>::cell_iterator &cell,\n              const Point<spacedim> &p,\n              const Point<dim> &initial_guess) const;\n\n    Point<spacedim> push_forward(\n        const typename Triangulation<dim, spacedim>::cell_iterator &cell,\n        const Point<dim> &chart_point) const;\n\n    DerivativeForm<1, dim, spacedim> push_forward_gradient(\n        const typename Triangulation<dim, spacedim>::cell_iterator &cell,\n        const Point<dim> &chart_point,\n        const Point<spacedim> &pushed_forward_chart_point) const;\n\n    Triangulation<dim, spacedim> triangulation;\n\n    int level_coarse;\n\n    std::vector<bool> coarse_cell_is_flat;\n\n    std::unique_ptr<Manifold<dim, spacedim>> chart_manifold;\n  };\n\n} // namespace ryujin\n"
  },
  {
    "path": "source/transfinite_interpolation.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception or LGPL-2.1-or-later\n// Copyright (C) 2007 - 2022 by Martin Kronbichler\n// Copyright (C) 2008 - 2022 by David Wells\n// Copyright (C) 2020 - 2023 by the ryujin authors\n//\n\n#pragma once\n\n#include \"transfinite_interpolation.h\"\n\n#include <boost/container/small_vector.hpp>\n\n#include <deal.II/base/table.h>\n\nnamespace ryujin\n{\n\n  namespace internal\n  {\n    static constexpr double invalid_pull_back_coordinate = 20.0;\n  }\n\n\n  template <int dim, int spacedim>\n  TransfiniteInterpolationManifold<dim,\n                                   spacedim>::TransfiniteInterpolationManifold()\n      : level_coarse(-1)\n  {\n    AssertThrow(dim > 1, ExcNotImplemented());\n  }\n\n\n  template <int dim, int spacedim>\n  std::unique_ptr<Manifold<dim, spacedim>>\n  TransfiniteInterpolationManifold<dim, spacedim>::clone() const\n  {\n    auto ptr = new TransfiniteInterpolationManifold<dim, spacedim>();\n    if (triangulation.n_levels() != 0)\n      ptr->initialize(triangulation, *chart_manifold);\n    return std::unique_ptr<Manifold<dim, spacedim>>(ptr);\n  }\n\n\n  template <int dim, int spacedim>\n  void TransfiniteInterpolationManifold<dim, spacedim>::initialize(\n      const Triangulation<dim, spacedim> &external_triangulation,\n      const Manifold<dim, spacedim> &chart_manifold)\n  {\n    this->triangulation.clear();\n    this->triangulation.copy_triangulation(external_triangulation);\n\n    this->chart_manifold = chart_manifold.clone();\n\n    level_coarse = triangulation.last()->level();\n    coarse_cell_is_flat.resize(triangulation.n_cells(level_coarse), false);\n    typename Triangulation<dim, spacedim>::active_cell_iterator\n        cell = triangulation.begin(level_coarse),\n        endc = triangulation.end(level_coarse);\n    for (; cell != endc; ++cell) {\n      bool cell_is_flat = true;\n      for (unsigned int l = 0; l < GeometryInfo<dim>::lines_per_cell; ++l)\n        if (cell->line(l)->manifold_id() != cell->manifold_id() &&\n            cell->line(l)->manifold_id() != numbers::flat_manifold_id)\n          cell_is_flat = false;\n      if (dim > 2)\n        for (unsigned int q = 0; q < GeometryInfo<dim>::quads_per_cell; ++q)\n          if (cell->quad(q)->manifold_id() != cell->manifold_id() &&\n              cell->quad(q)->manifold_id() != numbers::flat_manifold_id)\n            cell_is_flat = false;\n      AssertIndexRange(static_cast<unsigned int>(cell->index()),\n                       coarse_cell_is_flat.size());\n      coarse_cell_is_flat[cell->index()] = cell_is_flat;\n    }\n  }\n\n\n  namespace\n  {\n    // version for 1D\n    template <typename AccessorType>\n    Point<AccessorType::space_dimension>\n    compute_transfinite_interpolation(const AccessorType &cell,\n                                      const Point<1> &chart_point,\n                                      const bool /*cell_is_flat*/)\n    {\n      return cell.vertex(0) * (1. - chart_point[0]) +\n             cell.vertex(1) * chart_point[0];\n    }\n\n    // version for 2D\n    template <typename AccessorType>\n    Point<AccessorType::space_dimension>\n    compute_transfinite_interpolation(const AccessorType &cell,\n                                      const Point<2> &chart_point,\n                                      const bool cell_is_flat)\n    {\n      const unsigned int dim = AccessorType::dimension;\n      const unsigned int spacedim = AccessorType::space_dimension;\n      const types::manifold_id my_manifold_id = cell.manifold_id();\n      const Triangulation<dim, spacedim> &tria = cell.get_triangulation();\n\n      // formula see wikipedia\n      // https://en.wikipedia.org/wiki/Transfinite_interpolation\n      // S(u,v) = (1-v)c_1(u)+v c_3(u) + (1-u)c_2(v) + u c_4(v) -\n      //   [(1-u)(1-v)P_0 + u(1-v) P_1 + (1-u)v P_2 + uv P_3]\n      const std::array<Point<spacedim>, 4> vertices{\n          {cell.vertex(0), cell.vertex(1), cell.vertex(2), cell.vertex(3)}};\n\n      // this evaluates all bilinear shape functions because we need them\n      // repeatedly. we will update this values in the complicated case with\n      // curved lines below\n      std::array<double, 4> weights_vertices{\n          {(1. - chart_point[0]) * (1. - chart_point[1]),\n           chart_point[0] * (1. - chart_point[1]),\n           (1. - chart_point[0]) * chart_point[1],\n           chart_point[0] * chart_point[1]}};\n\n      Point<spacedim> new_point;\n      if (cell_is_flat)\n        for (const unsigned int v : GeometryInfo<2>::vertex_indices())\n          new_point += weights_vertices[v] * vertices[v];\n      else {\n        // The second line in the formula tells us to subtract the\n        // contribution of the vertices.  If a line employs the same manifold\n        // as the cell, we can merge the weights of the line with the weights\n        // of the vertex with a negative sign while going through the faces\n        // (this is a bit artificial in 2D but it becomes clear in 3D where we\n        // avoid looking at the faces' orientation and other complications).\n\n        // add the contribution from the lines around the cell (first line in\n        // formula)\n        std::array<double, GeometryInfo<2>::vertices_per_face> weights;\n        std::array<Point<spacedim>, GeometryInfo<2>::vertices_per_face> points;\n        // note that the views are immutable, but the arrays are not\n        const auto weights_view =\n            make_array_view(weights.begin(), weights.end());\n        const auto points_view = make_array_view(points.begin(), points.end());\n\n        for (unsigned int line = 0; line < GeometryInfo<2>::lines_per_cell;\n             ++line) {\n          const double my_weight =\n              (line % 2) ? chart_point[line / 2] : 1 - chart_point[line / 2];\n          const double line_point = chart_point[1 - line / 2];\n\n          // Same manifold or invalid id which will go back to the same\n          // class -> contribution should be added for the final point,\n          // which means that we subtract the current weight from the\n          // negative weight applied to the vertex\n          const types::manifold_id line_manifold_id =\n              cell.line(line)->manifold_id();\n          if (line_manifold_id == my_manifold_id ||\n              line_manifold_id == numbers::flat_manifold_id) {\n            weights_vertices[GeometryInfo<2>::line_to_cell_vertices(line, 0)] -=\n                my_weight * (1. - line_point);\n            weights_vertices[GeometryInfo<2>::line_to_cell_vertices(line, 1)] -=\n                my_weight * line_point;\n          } else {\n            points[0] =\n                vertices[GeometryInfo<2>::line_to_cell_vertices(line, 0)];\n            points[1] =\n                vertices[GeometryInfo<2>::line_to_cell_vertices(line, 1)];\n            weights[0] = 1. - line_point;\n            weights[1] = line_point;\n            new_point +=\n                my_weight * tria.get_manifold(line_manifold_id)\n                                .get_new_point(points_view, weights_view);\n          }\n        }\n\n        // subtract contribution from the vertices (second line in formula)\n        for (const unsigned int v : GeometryInfo<2>::vertex_indices())\n          new_point -= weights_vertices[v] * vertices[v];\n      }\n\n      return new_point;\n    }\n\n    // this is replicated from GeometryInfo::face_to_cell_vertices since we need\n    // it very often in compute_transfinite_interpolation and the function is\n    // performance critical\n    static constexpr unsigned int face_to_cell_vertices_3d[6][4] = {\n        {0, 2, 4, 6},\n        {1, 3, 5, 7},\n        {0, 4, 1, 5},\n        {2, 6, 3, 7},\n        {0, 1, 2, 3},\n        {4, 5, 6, 7}};\n\n    // this is replicated from GeometryInfo::face_to_cell_lines since we need it\n    // very often in compute_transfinite_interpolation and the function is\n    // performance critical\n    static constexpr unsigned int face_to_cell_lines_3d[6][4] = {{8, 10, 0, 4},\n                                                                 {9, 11, 1, 5},\n                                                                 {2, 6, 8, 9},\n                                                                 {3, 7, 10, 11},\n                                                                 {0, 1, 2, 3},\n                                                                 {4, 5, 6, 7}};\n\n    // version for 3D\n    template <typename AccessorType>\n    Point<AccessorType::space_dimension>\n    compute_transfinite_interpolation(const AccessorType &cell,\n                                      const Point<3> &chart_point,\n                                      const bool cell_is_flat)\n    {\n      const unsigned int dim = AccessorType::dimension;\n      const unsigned int spacedim = AccessorType::space_dimension;\n      const types::manifold_id my_manifold_id = cell.manifold_id();\n      const Triangulation<dim, spacedim> &tria = cell.get_triangulation();\n\n      // Same approach as in 2D, but adding the faces, subtracting the edges,\n      // and adding the vertices\n      const std::array<Point<spacedim>, 8> vertices{{cell.vertex(0),\n                                                     cell.vertex(1),\n                                                     cell.vertex(2),\n                                                     cell.vertex(3),\n                                                     cell.vertex(4),\n                                                     cell.vertex(5),\n                                                     cell.vertex(6),\n                                                     cell.vertex(7)}};\n\n      // store the components of the linear shape functions because we need them\n      // repeatedly. we allow for 10 such shape functions to wrap around the\n      // first four once again for easier face access.\n      double linear_shapes[10];\n      for (unsigned int d = 0; d < 3; ++d) {\n        linear_shapes[2 * d] = 1. - chart_point[d];\n        linear_shapes[2 * d + 1] = chart_point[d];\n      }\n\n      // wrap linear shape functions around for access in face loop\n      for (unsigned int d = 6; d < 10; ++d)\n        linear_shapes[d] = linear_shapes[d - 6];\n\n      std::array<double, 8> weights_vertices;\n      for (unsigned int i2 = 0, v = 0; i2 < 2; ++i2)\n        for (unsigned int i1 = 0; i1 < 2; ++i1)\n          for (unsigned int i0 = 0; i0 < 2; ++i0, ++v)\n            weights_vertices[v] =\n                (linear_shapes[4 + i2] * linear_shapes[2 + i1]) *\n                linear_shapes[i0];\n\n      Point<spacedim> new_point;\n      if (cell_is_flat)\n        for (unsigned int v = 0; v < 8; ++v)\n          new_point += weights_vertices[v] * vertices[v];\n      else {\n        // identify the weights for the lines to be accumulated (vertex\n        // weights are set outside and coincide with the flat manifold case)\n\n        std::array<double, GeometryInfo<3>::lines_per_cell> weights_lines;\n        std::fill(weights_lines.begin(), weights_lines.end(), 0.0);\n\n        // start with the contributions of the faces\n        std::array<double, GeometryInfo<2>::vertices_per_cell> weights;\n        std::array<Point<spacedim>, GeometryInfo<2>::vertices_per_cell> points;\n        // note that the views are immutable, but the arrays are not\n        const auto weights_view =\n            make_array_view(weights.begin(), weights.end());\n        const auto points_view = make_array_view(points.begin(), points.end());\n\n        for (const unsigned int face : GeometryInfo<3>::face_indices()) {\n          const double my_weight = linear_shapes[face];\n          const unsigned int face_even = face - face % 2;\n\n          if (std::abs(my_weight) < 1e-13)\n            continue;\n\n          // same manifold or invalid id which will go back to the same class\n          // -> face will interpolate from the surrounding lines and vertices\n          const types::manifold_id face_manifold_id =\n              cell.face(face)->manifold_id();\n          if (face_manifold_id == my_manifold_id ||\n              face_manifold_id == numbers::flat_manifold_id) {\n            for (unsigned int line = 0; line < GeometryInfo<2>::lines_per_cell;\n                 ++line) {\n              const double line_weight = linear_shapes[face_even + 2 + line];\n              weights_lines[face_to_cell_lines_3d[face][line]] +=\n                  my_weight * line_weight;\n            }\n            // as to the indices inside linear_shapes: we use the index\n            // wrapped around at 2*d, ensuring the correct orientation of\n            // the face's coordinate system with respect to the\n            // lexicographic indices\n            weights_vertices[face_to_cell_vertices_3d[face][0]] -=\n                linear_shapes[face_even + 2] *\n                (linear_shapes[face_even + 4] * my_weight);\n            weights_vertices[face_to_cell_vertices_3d[face][1]] -=\n                linear_shapes[face_even + 3] *\n                (linear_shapes[face_even + 4] * my_weight);\n            weights_vertices[face_to_cell_vertices_3d[face][2]] -=\n                linear_shapes[face_even + 2] *\n                (linear_shapes[face_even + 5] * my_weight);\n            weights_vertices[face_to_cell_vertices_3d[face][3]] -=\n                linear_shapes[face_even + 3] *\n                (linear_shapes[face_even + 5] * my_weight);\n          } else {\n            for (const unsigned int v : GeometryInfo<2>::vertex_indices())\n              points[v] = vertices[face_to_cell_vertices_3d[face][v]];\n            weights[0] =\n                linear_shapes[face_even + 2] * linear_shapes[face_even + 4];\n            weights[1] =\n                linear_shapes[face_even + 3] * linear_shapes[face_even + 4];\n            weights[2] =\n                linear_shapes[face_even + 2] * linear_shapes[face_even + 5];\n            weights[3] =\n                linear_shapes[face_even + 3] * linear_shapes[face_even + 5];\n            new_point +=\n                my_weight * tria.get_manifold(face_manifold_id)\n                                .get_new_point(points_view, weights_view);\n          }\n        }\n\n        // next subtract the contributions of the lines\n        const auto weights_view_line =\n            make_array_view(weights.begin(), weights.begin() + 2);\n        const auto points_view_line =\n            make_array_view(points.begin(), points.begin() + 2);\n        for (unsigned int line = 0; line < GeometryInfo<3>::lines_per_cell;\n             ++line) {\n          const double line_point =\n              (line < 8 ? chart_point[1 - (line % 4) / 2] : chart_point[2]);\n          double my_weight = 0.;\n          if (line < 8)\n            my_weight = linear_shapes[line % 4] * linear_shapes[4 + line / 4];\n          else {\n            const unsigned int subline = line - 8;\n            my_weight =\n                linear_shapes[subline % 2] * linear_shapes[2 + subline / 2];\n          }\n          my_weight -= weights_lines[line];\n\n          if (std::abs(my_weight) < 1e-13)\n            continue;\n\n          const types::manifold_id line_manifold_id =\n              cell.line(line)->manifold_id();\n          if (line_manifold_id == my_manifold_id ||\n              line_manifold_id == numbers::flat_manifold_id) {\n            weights_vertices[GeometryInfo<3>::line_to_cell_vertices(line, 0)] -=\n                my_weight * (1. - line_point);\n            weights_vertices[GeometryInfo<3>::line_to_cell_vertices(line, 1)] -=\n                my_weight * (line_point);\n          } else {\n            points[0] =\n                vertices[GeometryInfo<3>::line_to_cell_vertices(line, 0)];\n            points[1] =\n                vertices[GeometryInfo<3>::line_to_cell_vertices(line, 1)];\n            weights[0] = 1. - line_point;\n            weights[1] = line_point;\n            new_point -= my_weight * tria.get_manifold(line_manifold_id)\n                                         .get_new_point(points_view_line,\n                                                        weights_view_line);\n          }\n        }\n\n        // finally add the contribution of the\n        for (const unsigned int v : GeometryInfo<dim>::vertex_indices())\n          new_point += weights_vertices[v] * vertices[v];\n      }\n      return new_point;\n    }\n  } // namespace\n\n\n  template <int dim, int spacedim>\n  Point<spacedim> TransfiniteInterpolationManifold<dim, spacedim>::push_forward(\n      const typename Triangulation<dim, spacedim>::cell_iterator &cell,\n      const Point<dim> &chart_point) const\n  {\n    AssertDimension(cell->level(), level_coarse);\n\n    // check that the point is in the unit cell which is the current chart\n    // Tolerance 5e-4 chosen that the method also works with manifolds\n    // that have some discretization error like SphericalManifold\n    Assert(GeometryInfo<dim>::is_inside_unit_cell(chart_point, 5e-4),\n           ExcMessage(\"chart_point is not in unit interval\"));\n\n    return compute_transfinite_interpolation(\n        *cell, chart_point, coarse_cell_is_flat[cell->index()]);\n  }\n\n\n  template <int dim, int spacedim>\n  DerivativeForm<1, dim, spacedim>\n  TransfiniteInterpolationManifold<dim, spacedim>::push_forward_gradient(\n      const typename Triangulation<dim, spacedim>::cell_iterator &cell,\n      const Point<dim> &chart_point,\n      const Point<spacedim> &pushed_forward_chart_point) const\n  {\n    // compute the derivative with the help of finite differences\n    DerivativeForm<1, dim, spacedim> grad;\n    for (unsigned int d = 0; d < dim; ++d) {\n      Point<dim> modified = chart_point;\n      const double step = chart_point[d] > 0.5 ? -1e-8 : 1e-8;\n\n      // avoid checking outside of the unit interval\n      modified[d] += step;\n      Tensor<1, spacedim> difference =\n          compute_transfinite_interpolation(\n              *cell, modified, coarse_cell_is_flat[cell->index()]) -\n          pushed_forward_chart_point;\n      for (unsigned int e = 0; e < spacedim; ++e)\n        grad[e][d] = difference[e] / step;\n    }\n    return grad;\n  }\n\n\n  template <int dim, int spacedim>\n  Point<dim> TransfiniteInterpolationManifold<dim, spacedim>::pull_back(\n      const typename Triangulation<dim, spacedim>::cell_iterator &cell,\n      const Point<spacedim> &point,\n      const Point<dim> &initial_guess) const\n  {\n    Point<dim> outside;\n    for (unsigned int d = 0; d < dim; ++d)\n      outside[d] = internal::invalid_pull_back_coordinate;\n\n    // project the user-given input to unit cell\n    Point<dim> chart_point =\n        GeometryInfo<dim>::project_to_unit_cell(initial_guess);\n\n    // run quasi-Newton iteration with a combination of finite differences for\n    // the exact Jacobian and \"Broyden's good method\". As opposed to the various\n    // mapping implementations, this class does not throw exception upon failure\n    // as those are relatively expensive and failure occurs quite regularly in\n    // the implementation of the compute_chart_points method.\n    Tensor<1, spacedim> residual =\n        point - compute_transfinite_interpolation(\n                    *cell, chart_point, coarse_cell_is_flat[cell->index()]);\n    const double tolerance =\n        1e-21 * Utilities::fixed_power<2>(cell->diameter());\n    double residual_norm_square = residual.norm_square();\n    DerivativeForm<1, dim, spacedim> inv_grad;\n    bool must_recompute_jacobian = true;\n    for (unsigned int i = 0; i < 100; ++i) {\n      if (residual_norm_square < tolerance) {\n        // do a final update of the point with the last available Jacobian\n        // information. The residual is close to zero due to the check\n        // above, but me might improve some of the last digits by a final\n        // Newton-like step with step length 1\n        Tensor<1, dim> update;\n        for (unsigned int d = 0; d < spacedim; ++d)\n          for (unsigned int e = 0; e < dim; ++e)\n            update[e] += inv_grad[d][e] * residual[d];\n        return chart_point + update;\n      }\n\n      // every 9 iterations, including the first time around, we create an\n      // approximation of the Jacobian with finite differences. Broyden's\n      // method usually does not need more than 5-8 iterations, but sometimes\n      // we might have had a bad initial guess and then we can accelerate\n      // convergence considerably with getting the actual Jacobian rather than\n      // using secant-like methods (one gradient calculation in 3D costs as\n      // much as 3 more iterations). this usually happens close to convergence\n      // and one more step with the finite-differenced Jacobian leads to\n      // convergence. however, we should not make the update too close to\n      // termination either because of cancellation effects an finite\n      // difference accuracy.\n      if (must_recompute_jacobian ||\n          (residual_norm_square > 1e4 * tolerance && i % 7 == 0)) {\n        // if the determinant is zero or negative, the mapping is either not\n        // invertible or already has inverted and we are outside the valid\n        // chart region. Note that the Jacobian here represents the\n        // derivative of the forward map and should have a positive\n        // determinant since we use properly oriented meshes.\n        DerivativeForm<1, dim, spacedim> grad = push_forward_gradient(\n            cell, chart_point, Point<spacedim>(point - residual));\n        if (grad.determinant() <= 0.0)\n          return outside;\n        inv_grad = grad.covariant_form();\n        must_recompute_jacobian = false;\n      }\n      Tensor<1, dim> update;\n      for (unsigned int d = 0; d < spacedim; ++d)\n        for (unsigned int e = 0; e < dim; ++e)\n          update[e] += inv_grad[d][e] * residual[d];\n\n      // Line search, accept step if the residual has decreased\n      double alpha = 1.;\n\n      // check if point is inside 1.2 times the unit cell to avoid\n      // hitting points very far away from valid ones in the manifolds\n      while (!GeometryInfo<dim>::is_inside_unit_cell(\n                 chart_point + alpha * update, 0.2) &&\n             alpha > 1e-7)\n        alpha *= 0.5;\n\n      const Tensor<1, spacedim> old_residual = residual;\n      while (alpha > 1e-4) {\n        Point<dim> guess = chart_point + alpha * update;\n        const Tensor<1, dim> residual_guess =\n            point - compute_transfinite_interpolation(\n                        *cell, guess, coarse_cell_is_flat[cell->index()]);\n        const double residual_norm_new = residual_guess.norm_square();\n        if (residual_norm_new < residual_norm_square) {\n          residual_norm_square = residual_norm_new;\n          chart_point += alpha * update;\n          residual = residual_guess;\n          break;\n        } else\n          alpha *= 0.5;\n      }\n      // If alpha got very small, it is likely due to a bad Jacobian\n      // approximation with Broyden's method (relatively far away from the\n      // zero), which can be corrected by the outer loop when a Newton update\n      // is recomputed. The second case is when we either have roundoff errors\n      // and cannot further improve the approximation or the Jacobian is\n      // actually bad and we should fail as early as possible. Since we cannot\n      // really distinguish the two, we must continue here in any case.\n      if (alpha <= 1e-4) {\n        // If we just recomputed the Jacobian by finite differences, we must\n        // stop. If the reached tolerance was sufficiently small (less than\n        // the square root of the tolerance), we return the best estimate,\n        // else we return the invalid point.\n        if (must_recompute_jacobian == true) {\n          return residual_norm_square < std::sqrt(tolerance) ? chart_point\n                                                             : outside;\n        } else\n          must_recompute_jacobian = true;\n      }\n\n      // update the inverse Jacobian with \"Broyden's good method\" and\n      // Sherman-Morrison formula for the update of the inverse, see\n      // https://en.wikipedia.org/wiki/Broyden%27s_method\n      // J^{-1}_n = J^{-1}_{n-1} + (delta x_n - J^{-1}_{n-1} delta f_n) /\n      // (delta x_n^T J_{-1}_{n-1} delta f_n) delta x_n^T J^{-1}_{n-1}\n\n      // switch sign in residual as compared to the formula above because we\n      // use a negative definition of the residual with respect to the\n      // Jacobian\n      const Tensor<1, spacedim> delta_f = old_residual - residual;\n\n      Tensor<1, dim> Jinv_deltaf;\n      for (unsigned int d = 0; d < spacedim; ++d)\n        for (unsigned int e = 0; e < dim; ++e)\n          Jinv_deltaf[e] += inv_grad[d][e] * delta_f[d];\n\n      const Tensor<1, dim> delta_x = alpha * update;\n\n      // prevent division by zero. This number should be scale-invariant\n      // because Jinv_deltaf carries no units and x is in reference\n      // coordinates.\n      if (std::abs(delta_x * Jinv_deltaf) > 0.1 * tolerance &&\n          !must_recompute_jacobian) {\n        const Tensor<1, dim> factor =\n            (delta_x - Jinv_deltaf) / (delta_x * Jinv_deltaf);\n        Tensor<1, spacedim> jac_update;\n        for (unsigned int d = 0; d < spacedim; ++d)\n          for (unsigned int e = 0; e < dim; ++e)\n            jac_update[d] += delta_x[e] * inv_grad[d][e];\n        for (unsigned int d = 0; d < spacedim; ++d)\n          for (unsigned int e = 0; e < dim; ++e)\n            inv_grad[d][e] += factor[e] * jac_update[d];\n      }\n    }\n    return outside;\n  }\n\n\n  template <int dim, int spacedim>\n  std::array<unsigned int, 20> TransfiniteInterpolationManifold<dim, spacedim>::\n      get_possible_cells_around_points(\n          const ArrayView<const Point<spacedim>> &points) const\n  {\n    // The methods to identify cells around points in GridTools are all written\n    // for the active cells, but we are here looking at some cells at the coarse\n    // level.\n\n    // FIXME IMPROVE\n\n    Assert(triangulation.n_levels() != 0, ExcNotInitialized());\n    Assert(triangulation.begin_active()->level() == level_coarse,\n           ExcInternalError());\n\n    // This computes the distance of the surrounding points transformed to the\n    // unit cell from the unit cell.\n    auto cell = triangulation.begin(level_coarse);\n    const auto endc = triangulation.end(level_coarse);\n    boost::container::small_vector<std::pair<double, unsigned int>, 200>\n        distances_and_cells;\n    for (; cell != endc; ++cell) {\n      /* FIXME: Remove workaround - ignore certain cells. */\n      if (cell->material_id() == 42)\n        continue;\n\n      std::array<Point<spacedim>, GeometryInfo<dim>::vertices_per_cell>\n          vertices;\n      for (const unsigned int vertex_n : GeometryInfo<dim>::vertex_indices()) {\n        vertices[vertex_n] = cell->vertex(vertex_n);\n      }\n\n      // cheap check: if any of the points is not inside a circle around the\n      // center of the loop, we can skip the expensive part below (this assumes\n      // that the manifold does not deform the grid too much)\n      Point<spacedim> center;\n      for (const unsigned int v : GeometryInfo<dim>::vertex_indices())\n        center += vertices[v];\n      center *= 1. / GeometryInfo<dim>::vertices_per_cell;\n      double radius_square = 0.;\n      for (const unsigned int v : GeometryInfo<dim>::vertex_indices())\n        radius_square =\n            std::max(radius_square, (center - vertices[v]).norm_square());\n      bool inside_circle = true;\n      for (unsigned int i = 0; i < points.size(); ++i)\n        if ((center - points[i]).norm_square() > radius_square * 1.5) {\n          inside_circle = false;\n          break;\n        }\n      if (inside_circle == false)\n        continue;\n\n      // slightly more expensive search\n      double current_distance = 0;\n      for (unsigned int i = 0; i < points.size(); ++i) {\n        Point<dim> point =\n            cell->real_to_unit_cell_affine_approximation(points[i]);\n        current_distance += GeometryInfo<dim>::distance_to_unit_cell(point);\n      }\n      distances_and_cells.push_back(\n          std::make_pair(current_distance, cell->index()));\n    }\n    // no coarse cell could be found -> transformation failed\n    AssertThrow(distances_and_cells.size() > 0,\n                (typename Mapping<dim, spacedim>::ExcTransformationFailed()));\n    std::sort(distances_and_cells.begin(), distances_and_cells.end());\n    std::array<unsigned int, 20> cells;\n    cells.fill(numbers::invalid_unsigned_int);\n    for (unsigned int i = 0; i < distances_and_cells.size() && i < cells.size();\n         ++i)\n      cells[i] = distances_and_cells[i].second;\n\n    return cells;\n  }\n\n\n  template <int dim, int spacedim>\n  typename Triangulation<dim, spacedim>::cell_iterator\n  TransfiniteInterpolationManifold<dim, spacedim>::compute_chart_points(\n      const ArrayView<const Point<spacedim>> &surrounding_points,\n      ArrayView<Point<dim>> chart_points) const\n  {\n    Assert(surrounding_points.size() == chart_points.size(),\n           ExcMessage(\"The chart points array view must be as large as the \"\n                      \"surrounding points array view.\"));\n\n    std::array<unsigned int, 20> nearby_cells =\n        get_possible_cells_around_points(surrounding_points);\n\n    // This function is nearly always called to place new points on a cell or\n    // cell face. In this case, the general structure of the surrounding points\n    // is known (i.e., if there are eight surrounding points, then they will\n    // almost surely be either eight points around a quadrilateral or the eight\n    // vertices of a cube). Hence, making this assumption, we use two\n    // optimizations (one for structdim == 2 and one for structdim == 3) that\n    // guess the locations of some of the chart points more efficiently than the\n    // affine map approximation. The affine map approximation is used whenever\n    // we don't have a cheaper guess available.\n\n    // Function that can guess the location of a chart point by assuming that\n    // the eight surrounding points are points on a two-dimensional object\n    // (either a cell in 2D or the face of a hexahedron in 3D), arranged like\n    //\n    //     2 - 7 - 3\n    //     |       |\n    //     4       5\n    //     |       |\n    //     0 - 6 - 1\n    //\n    // This function assumes that the first three chart points have been\n    // computed since there is no effective way to guess them.\n    auto guess_chart_point_structdim_2 =\n        [&](const unsigned int i) -> Point<dim> {\n      Assert(\n          surrounding_points.size() == 8 && 2 < i && i < 8,\n          ExcMessage(\"This function assumes that there are eight surrounding \"\n                     \"points around a two-dimensional object. It also assumes \"\n                     \"that the first three chart points have already been \"\n                     \"computed.\"));\n      switch (i) {\n      case 0:\n      case 1:\n      case 2:\n        Assert(false, ExcInternalError());\n        break;\n      case 3:\n        return chart_points[1] + (chart_points[2] - chart_points[0]);\n      case 4:\n        return 0.5 * (chart_points[0] + chart_points[2]);\n      case 5:\n        return 0.5 * (chart_points[1] + chart_points[3]);\n      case 6:\n        return 0.5 * (chart_points[0] + chart_points[1]);\n      case 7:\n        return 0.5 * (chart_points[2] + chart_points[3]);\n      default:\n        Assert(false, ExcInternalError());\n      }\n\n      return Point<dim>();\n    };\n\n    // Function that can guess the location of a chart point by assuming that\n    // the eight surrounding points form the vertices of a hexahedron, arranged\n    // like\n    //\n    //         6-------7\n    //        /|      /|\n    //       /       / |\n    //      /  |    /  |\n    //     4-------5   |\n    //     |   2- -|- -3\n    //     |  /    |  /\n    //     |       | /\n    //     |/      |/\n    //     0-------1\n    //\n    // (where vertex 2 is the back left vertex) we can estimate where chart\n    // points 5 - 7 are by computing the height (in chart coordinates) as c4 -\n    // c0 and then adding that onto the appropriate bottom vertex.\n    //\n    // This function assumes that the first five chart points have been computed\n    // since there is no effective way to guess them.\n    auto guess_chart_point_structdim_3 =\n        [&](const unsigned int i) -> Point<dim> {\n      Assert(\n          surrounding_points.size() == 8 && 4 < i && i < 8,\n          ExcMessage(\"This function assumes that there are eight surrounding \"\n                     \"points around a three-dimensional object. It also \"\n                     \"assumes that the first five chart points have already \"\n                     \"been computed.\"));\n      return chart_points[i - 4] + (chart_points[4] - chart_points[0]);\n    };\n\n    // Check if we can use the two chart point shortcuts above before we start:\n    bool use_structdim_2_guesses = false;\n    bool use_structdim_3_guesses = false;\n    // note that in the structdim 2 case: 0 - 6 and 2 - 7 should be roughly\n    // parallel, while in the structdim 3 case, 0 - 6 and 2 - 7 should be\n    // roughly orthogonal. Use the angle between these two vectors to figure out\n    // if we should turn on either structdim optimization.\n    if (surrounding_points.size() == 8) {\n      const Tensor<1, spacedim> v06 =\n          surrounding_points[6] - surrounding_points[0];\n      const Tensor<1, spacedim> v27 =\n          surrounding_points[7] - surrounding_points[2];\n\n      // note that we can save a call to sqrt() by rearranging\n      const double cosine = scalar_product(v06, v27) /\n                            std::sqrt(v06.norm_square() * v27.norm_square());\n      if (0.707 < cosine)\n        // the angle is less than pi/4, so these vectors are roughly parallel:\n        // enable the structdim 2 optimization\n        use_structdim_2_guesses = true;\n      else if (spacedim == 3)\n        // otherwise these vectors are roughly orthogonal: enable the\n        // structdim 3 optimization if we are in 3D\n        use_structdim_3_guesses = true;\n    }\n    // we should enable at most one of the optimizations\n    Assert((!use_structdim_2_guesses && !use_structdim_3_guesses) ||\n               (use_structdim_2_guesses ^ use_structdim_3_guesses),\n           ExcInternalError());\n\n\n    auto compute_chart_point =\n        [&](const typename Triangulation<dim, spacedim>::cell_iterator &cell,\n            const unsigned int point_index) {\n          Point<dim> guess;\n          // an optimization: keep track of whether or not we used the affine\n          // approximation so that we don't call pull_back with the same\n          // initial guess twice (i.e., if pull_back fails the first time,\n          // don't try again with the same function arguments).\n          bool used_affine_approximation = false;\n          // if we have already computed three points, we can guess the fourth\n          // to be the missing corner point of a rectangle\n          if (point_index == 3 && surrounding_points.size() >= 8)\n            guess = chart_points[1] + (chart_points[2] - chart_points[0]);\n          else if (use_structdim_2_guesses && 3 < point_index)\n            guess = guess_chart_point_structdim_2(point_index);\n          else if (use_structdim_3_guesses && 4 < point_index)\n            guess = guess_chart_point_structdim_3(point_index);\n          else if (dim == 3 && point_index > 7 &&\n                   surrounding_points.size() == 26) {\n            if (point_index < 20)\n              guess =\n                  0.5 * (chart_points[GeometryInfo<dim>::line_to_cell_vertices(\n                             point_index - 8, 0)] +\n                         chart_points[GeometryInfo<dim>::line_to_cell_vertices(\n                             point_index - 8, 1)]);\n            else\n              guess =\n                  0.25 * (chart_points[GeometryInfo<dim>::face_to_cell_vertices(\n                              point_index - 20, 0)] +\n                          chart_points[GeometryInfo<dim>::face_to_cell_vertices(\n                              point_index - 20, 1)] +\n                          chart_points[GeometryInfo<dim>::face_to_cell_vertices(\n                              point_index - 20, 2)] +\n                          chart_points[GeometryInfo<dim>::face_to_cell_vertices(\n                              point_index - 20, 3)]);\n          } else {\n            guess = cell->real_to_unit_cell_affine_approximation(\n                surrounding_points[point_index]);\n            used_affine_approximation = true;\n          }\n          chart_points[point_index] =\n              pull_back(cell, surrounding_points[point_index], guess);\n\n          // the initial guess may not have been good enough: if applicable,\n          // try again with the affine approximation (which is more accurate\n          // than the cheap methods used above)\n          if (chart_points[point_index][0] ==\n                  internal::invalid_pull_back_coordinate &&\n              !used_affine_approximation) {\n            guess = cell->real_to_unit_cell_affine_approximation(\n                surrounding_points[point_index]);\n            chart_points[point_index] =\n                pull_back(cell, surrounding_points[point_index], guess);\n          }\n\n          if (chart_points[point_index][0] ==\n              internal::invalid_pull_back_coordinate) {\n            for (unsigned int d = 0; d < dim; ++d)\n              guess[d] = 0.5;\n            chart_points[point_index] =\n                pull_back(cell, surrounding_points[point_index], guess);\n          }\n        };\n\n    // check whether all points are inside the unit cell of the current chart\n    for (unsigned int c = 0; c < nearby_cells.size(); ++c) {\n      typename Triangulation<dim, spacedim>::cell_iterator cell(\n          &triangulation, level_coarse, nearby_cells[c]);\n      bool inside_unit_cell = true;\n      for (unsigned int i = 0; i < surrounding_points.size(); ++i) {\n        compute_chart_point(cell, i);\n\n        // Tolerance 5e-4 chosen that the method also works with manifolds\n        // that have some discretization error like SphericalManifold\n        if (GeometryInfo<dim>::is_inside_unit_cell(chart_points[i], 5e-4) ==\n            false) {\n          inside_unit_cell = false;\n          break;\n        }\n      }\n      if (inside_unit_cell == true) {\n        return cell;\n      }\n\n      // if we did not find a point and this was the last valid cell (the next\n      // iterate being the end of the array or an invalid tag), we must stop\n      if (c == nearby_cells.size() - 1 ||\n          nearby_cells[c + 1] == numbers::invalid_unsigned_int) {\n        // generate additional information to help debugging why we did not\n        // get a point\n        std::ostringstream message;\n        for (unsigned int b = 0; b <= c; ++b) {\n          typename Triangulation<dim, spacedim>::cell_iterator cell(\n              &triangulation, level_coarse, nearby_cells[b]);\n          message << \"Looking at cell \" << cell->id()\n                  << \" with vertices: \" << std::endl;\n          for (const unsigned int v : GeometryInfo<dim>::vertex_indices())\n            message << std::setprecision(16) << \" \" << cell->vertex(v)\n                    << \"    \";\n          message << std::endl;\n          message << \"Transformation to chart coordinates: \" << std::endl;\n          for (unsigned int i = 0; i < surrounding_points.size(); ++i) {\n            compute_chart_point(cell, i);\n            message << std::setprecision(16) << surrounding_points[i] << \" -> \"\n                    << chart_points[i] << std::endl;\n          }\n        }\n\n        AssertThrow(false,\n                    (typename Mapping<dim, spacedim>::ExcTransformationFailed(\n                        message.str())));\n      }\n    }\n\n    // a valid inversion should have returned a point above. an invalid\n    // inversion should have triggered the assertion, so we should never end up\n    // here\n    Assert(false, ExcInternalError());\n    return typename Triangulation<dim, spacedim>::cell_iterator();\n  }\n\n\n  template <int dim, int spacedim>\n  Point<spacedim>\n  TransfiniteInterpolationManifold<dim, spacedim>::get_new_point(\n      const ArrayView<const Point<spacedim>> &surrounding_points,\n      const ArrayView<const double> &weights) const\n  {\n    boost::container::small_vector<Point<dim>, 100> chart_points(\n        surrounding_points.size());\n    ArrayView<Point<dim>> chart_points_view =\n        make_array_view(chart_points.begin(), chart_points.end());\n    const auto cell =\n        compute_chart_points(surrounding_points, chart_points_view);\n\n    const Point<dim> p_chart =\n        chart_manifold->get_new_point(chart_points_view, weights);\n\n    return push_forward(cell, p_chart);\n  }\n\n\n  template <int dim, int spacedim>\n  void TransfiniteInterpolationManifold<dim, spacedim>::get_new_points(\n      const ArrayView<const Point<spacedim>> &surrounding_points,\n      const Table<2, double> &weights,\n      ArrayView<Point<spacedim>> new_points) const\n  {\n    Assert(weights.size(0) > 0, ExcEmptyObject());\n    AssertDimension(surrounding_points.size(), weights.size(1));\n\n    boost::container::small_vector<Point<dim>, 100> chart_points(\n        surrounding_points.size());\n    ArrayView<Point<dim>> chart_points_view =\n        make_array_view(chart_points.begin(), chart_points.end());\n    const auto cell =\n        compute_chart_points(surrounding_points, chart_points_view);\n\n    boost::container::small_vector<Point<dim>, 100> new_points_on_chart(\n        weights.size(0));\n    chart_manifold->get_new_points(chart_points_view,\n                                   weights,\n                                   make_array_view(new_points_on_chart.begin(),\n                                                   new_points_on_chart.end()));\n\n    for (unsigned int row = 0; row < weights.size(0); ++row)\n      new_points[row] = push_forward(cell, new_points_on_chart[row]);\n  }\n\n} // namespace ryujin\n"
  },
  {
    "path": "source/version_info.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2024 by the ryujin authors\n//\n\n#include \"version_info.h\"\n#include \"compile_time_options.h\"\n\n#include <deal.II/base/revision.h>\n#include <deal.II/base/vectorization.h>\n\n#include <iomanip>\n#include <iostream>\n\nnamespace ryujin\n{\n  void print_revision_and_version(std::ostream &stream)\n  {\n    /* clang-format off */\n    stream << std::endl;\n    stream << \"###\" << std::endl;\n    stream << \"#\" << std::endl;\n    stream << \"# deal.II version \" << std::setw(8) << DEAL_II_PACKAGE_VERSION\n            << \"  -  \" << DEAL_II_GIT_REVISION << std::endl;\n    stream << \"# ryujin  version \" << std::setw(8) << RYUJIN_VERSION\n            << \"  -  \" << RYUJIN_GIT_REVISION << std::endl;\n    stream << \"#\" << std::endl;\n    stream << \"###\" << std::endl;\n    /* clang-format on */\n  }\n} // namespace ryujin\n"
  },
  {
    "path": "source/version_info.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2024 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include <ostream>\n\nnamespace ryujin\n{\n  /**\n   * Print git revision and version info.\n   */\n  void print_revision_and_version(std::ostream &stream);\n} // namespace ryujin\n"
  },
  {
    "path": "source/vtu_output.cc",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2023 by the ryujin authors\n//\n\n#include \"vtu_output.template.h\"\n#include <instantiate.h>\n\nnamespace ryujin\n{\n  /* instantiations */\n  template class VTUOutput<Description, 1, NUMBER>;\n  template class VTUOutput<Description, 2, NUMBER>;\n  template class VTUOutput<Description, 3, NUMBER>;\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/vtu_output.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2025 by the ryujin authors\n//\n\n#pragma once\n\n#include <compile_time_options.h>\n\n#include \"mpi_ensemble.h\"\n#include \"observer_pointer.h\"\n#include \"offline_data.h\"\n#include \"postprocessor.h\"\n\n#include <deal.II/base/parameter_acceptor.h>\n#include <deal.II/grid/intergrid_map.h>\n#include <deal.II/multigrid/mg_transfer_matrix_free.h>\n\nnamespace ryujin\n{\n\n  /**\n   * the VTUOutput class implements output of the conserved state vector\n   * and a number of postprocessed quantities computed by the Postprocessor\n   * class.\n   *\n   * @ingroup TimeLoop\n   */\n  template <typename Description, int dim, typename Number = double>\n  class VTUOutput final : public dealii::ParameterAcceptor\n  {\n  public:\n    /**\n     * @name Typedefs and constexpr constants\n     */\n    //@{\n\n    using HyperbolicSystem = typename Description::HyperbolicSystem;\n    using ParabolicSystem = typename Description::ParabolicSystem;\n\n    using View =\n        typename Description::template HyperbolicSystemView<dim, Number>;\n\n    static constexpr auto problem_dimension = View::problem_dimension;\n\n    using state_type = typename View::state_type;\n\n    static constexpr auto n_precomputed_values = View::n_precomputed_values;\n\n    using precomputed_type = typename View::precomputed_type;\n\n    using StateVector = typename View::StateVector;\n    using InitialPrecomputedVector = typename View::InitialPrecomputedVector;\n    using ScalarVector = Vectors::ScalarVector<Number>;\n\n    //@}\n    /**\n     * @name Constructor and setup\n     */\n    //@{\n\n    /**\n     * Constructor.\n     */\n    VTUOutput(const MPIEnsemble &mpi_ensemble,\n              const OfflineData<dim, Number> &offline_data,\n              const HyperbolicSystem &hyperbolic_system,\n              const ParabolicSystem &parabolic_system,\n              const Postprocessor<Description, dim, Number> &postprocessor,\n              const InitialPrecomputedVector &initial_precomputed,\n              const ScalarVector &alpha,\n              const ScalarVector &smoothness_indicators,\n              const std::string &subsection = \"/VTUOutput\");\n\n    /**\n     * Prepare VTU output. A call to @ref prepare() allocates temporary\n     * storage and is necessary before schedule_output() can be called.\n     *\n     * Calling prepare() allocates temporary storage for additional (dim +\n     * 5) scalar vectors of type OfflineData::scalar_type.\n     */\n    void prepare();\n\n    /**\n     * Given a state vector @p U and a file name prefix @p name, the\n     * current time @p t, and the current output cycle @p cycle) schedule a\n     * solution output.\n     *\n     * The function post-processes quantities synchronously, and (depending\n     * on configuration options) schedules the write-out asynchronously\n     * onto a background worker thread. This implies that @p U can again be\n     * modified once schedule_output() returned.\n     *\n     * The booleans @p output_full controls whether the full vector field\n     * is written out. Correspondingly, @p output_cutplanes controls\n     * whether cells in the vicinity of predefined cutplanes are written\n     * out.\n     *\n     * The function requires MPI communication and is not reentrant.\n     */\n    void schedule_output(const StateVector &state_vector,\n                         std::string name,\n                         Number t,\n                         unsigned int cycle,\n                         bool output_full = true,\n                         bool output_cutplanes = true);\n\n  private:\n    /**\n     * @name Run time options\n     */\n    //@{\n\n    bool use_mpi_io_;\n\n    std::vector<std::string> manifolds_;\n\n    std::vector<std::string> vtu_output_quantities_;\n\n    //@}\n    /**\n     * @name Internal data\n     */\n    //@{\n\n    const MPIEnsemble &mpi_ensemble_;\n\n    dealii::ObserverPointer<const OfflineData<dim, Number>> offline_data_;\n    dealii::ObserverPointer<const HyperbolicSystem> hyperbolic_system_;\n    dealii::ObserverPointer<const ParabolicSystem> parabolic_system_;\n    dealii::ObserverPointer<const Postprocessor<Description, dim, Number>>\n        postprocessor_;\n\n    const InitialPrecomputedVector &initial_precomputed_;\n    const ScalarVector &alpha_;\n    const ScalarVector &smoothness_indicators_;\n    //@}\n  };\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "source/vtu_output.template.h",
    "content": "//\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// Copyright (C) 2020 - 2026 by the ryujin authors\n//\n\n#pragma once\n\n#include \"selected_components_extractor.h\"\n#include \"vtu_output.h\"\n\n#include <deal.II/base/function_parser.h>\n#include <deal.II/numerics/data_out.h>\n#include <deal.II/numerics/vector_tools.h>\n\n\nnamespace ryujin\n{\n  using namespace dealii;\n\n\n  template <typename Description, int dim, typename Number>\n  VTUOutput<Description, dim, Number>::VTUOutput(\n      const MPIEnsemble &mpi_ensemble,\n      const OfflineData<dim, Number> &offline_data,\n      const HyperbolicSystem &hyperbolic_system,\n      const ParabolicSystem &parabolic_system,\n      const Postprocessor<Description, dim, Number> &postprocessor,\n      const InitialPrecomputedVector &initial_precomputed,\n      const ScalarVector &alpha,\n      const ScalarVector &smoothness_indicators,\n      const std::string &subsection /*= \"VTUOutput\"*/)\n      : ParameterAcceptor(subsection)\n      , mpi_ensemble_(mpi_ensemble)\n      , offline_data_(&offline_data)\n      , hyperbolic_system_(&hyperbolic_system)\n      , parabolic_system_(&parabolic_system)\n      , postprocessor_(&postprocessor)\n      , initial_precomputed_(initial_precomputed)\n      , alpha_(alpha)\n      , smoothness_indicators_(smoothness_indicators)\n  {\n    use_mpi_io_ = true;\n    add_parameter(\"use mpi io\",\n                  use_mpi_io_,\n                  \"If enabled write out one vtu file via MPI IO using \"\n                  \"write_vtu_in_parallel() instead of independent output files \"\n                  \"via write_vtu_with_pvtu_record()\");\n\n    add_parameter(\"manifolds\",\n                  manifolds_,\n                  \"List of level set functions. The description is used to \"\n                  \"only output cells that intersect the given level set.\");\n\n    std::copy(std::begin(View::component_names),\n              std::end(View::component_names),\n              std::back_inserter(vtu_output_quantities_));\n\n    std::copy(std::begin(View::initial_precomputed_names),\n              std::end(View::initial_precomputed_names),\n              std::back_inserter(vtu_output_quantities_));\n\n    add_parameter(\"vtu output quantities\",\n                  vtu_output_quantities_,\n                  \"List of conserved, primitive, precomputed, or postprocessed \"\n                  \"quantities that will be written to the vtu files.\");\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void VTUOutput<Description, dim, Number>::prepare()\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"VTUOutput<dim, Number>::prepare()\" << std::endl;\n#endif\n\n    SelectedComponentsExtractor<Description, dim, Number>::check(\n        parabolic_system_->parabolic_component_names(),\n        {\"alpha\", \"smoothness_indicators\"},\n        vtu_output_quantities_);\n  }\n\n\n  template <typename Description, int dim, typename Number>\n  void VTUOutput<Description, dim, Number>::schedule_output(\n      const StateVector &state_vector,\n      std::string name,\n      Number t [[maybe_unused]],\n      unsigned int cycle,\n      bool output_full,\n      bool output_levelsets)\n  {\n#ifdef DEBUG_OUTPUT\n    std::cout << \"VTUOutput<dim, Number>::schedule_output()\" << std::endl;\n#endif\n\n    /*\n     * Extract quantities and store in ScalarHostVectors so that we can\n     * call DataOut::add_data_vector()\n     */\n\n    auto selected_components =\n        SelectedComponentsExtractor<Description, dim, Number>::extract(\n            *offline_data_,\n            *hyperbolic_system_,\n            *parabolic_system_,\n            state_vector,\n            initial_precomputed_,\n            {\"alpha\", \"smoothness_indicators\"},\n            {alpha_, smoothness_indicators_},\n            vtu_output_quantities_);\n\n    /*\n     * Attach data vectors to DataOut object:\n     */\n\n    auto data_out = std::make_unique<dealii::DataOut<dim>>();\n\n    const auto attach_data_vector = [&](auto &data, const auto &name) {\n      const auto &dof_handler_cg = offline_data_->dof_handler_cg();\n      const auto &dof_handler_dg = offline_data_->dof_handler_dg();\n\n      if (data.size() == dof_handler_cg.n_dofs()) {\n        offline_data_->affine_constraints_cg().distribute(data);\n        data.update_ghost_values();\n        data_out->add_data_vector(dof_handler_cg, data, name);\n\n      } else if (data.size() == dof_handler_dg.n_dofs()) {\n        offline_data_->affine_constraints_dg().distribute(data);\n        data.update_ghost_values();\n        data_out->add_data_vector(dof_handler_dg, data, name);\n\n      } else {\n        Assert(\n            false,\n            dealii::ExcMessage(\"The selected solution component »\" + name +\n                               \"« is associated with an unknown dof handler\"));\n      }\n    };\n\n    for (unsigned int d = 0; d < selected_components.size(); ++d) {\n      attach_data_vector(selected_components[d], vtu_output_quantities_[d]);\n    }\n\n    const auto n_quantities = postprocessor_->n_quantities();\n    for (unsigned int i = 0; i < n_quantities; ++i) {\n      // FIXME maybe also refactor to use attach_data_vector()\n      data_out->add_data_vector(offline_data_->dof_handler(),\n                                postprocessor_->quantities()[i],\n                                postprocessor_->component_names()[i]);\n    }\n\n    DataOutBase::VtkFlags flags(\n        t, cycle, true, DataOutBase::CompressionLevel::best_speed);\n    data_out->set_flags(flags);\n\n    const auto &discretization = offline_data_->discretization();\n    const auto &mapping = discretization.mapping();\n    const auto patch_order =\n        std::max(1u, discretization.polynomial_degree()) - 1u;\n\n    /* Perform output: */\n\n    if (output_full) {\n      data_out->build_patches(mapping, patch_order);\n\n      if (use_mpi_io_) {\n        /* MPI-based synchronous IO */\n        data_out->write_vtu_in_parallel(\n            name + \"_\" + Utilities::to_string(cycle, 6) + \".vtu\",\n            mpi_ensemble_.ensemble_communicator());\n      } else {\n        data_out->write_vtu_with_pvtu_record(\n            \"\", name, cycle, mpi_ensemble_.ensemble_communicator(), 6);\n      }\n    }\n\n    if (output_levelsets && manifolds_.size() != 0) {\n      /*\n       * Specify an output filter that selects only cells for output that are\n       * in the viscinity of a specified set of output planes:\n       */\n\n      std::vector<std::shared_ptr<FunctionParser<dim>>> level_set_functions;\n      for (const auto &expression : manifolds_)\n        level_set_functions.emplace_back(\n            std::make_shared<FunctionParser<dim>>(expression));\n\n      data_out->set_cell_selection([level_set_functions](const auto &cell) {\n        if (!cell->is_active() || cell->is_artificial())\n          return false;\n\n        for (const auto &function : level_set_functions) {\n\n          unsigned int above = 0;\n          unsigned int below = 0;\n\n          for (unsigned int v : cell->vertex_indices()) {\n            const auto vertex = cell->vertex(v);\n            constexpr auto eps = std::numeric_limits<Number>::epsilon();\n            if (function->value(vertex) >= 0. - 100. * eps)\n              above++;\n            if (function->value(vertex) <= 0. + 100. * eps)\n              below++;\n            if (above > 0 && below > 0)\n              return true;\n          }\n        }\n        return false;\n      });\n\n      data_out->build_patches(mapping, patch_order);\n\n      if (use_mpi_io_) {\n        /* MPI-based synchronous IO */\n        data_out->write_vtu_in_parallel(\n            name + \"-levelsets_\" + Utilities::to_string(cycle, 6) + \".vtu\",\n            mpi_ensemble_.ensemble_communicator());\n      } else {\n        data_out->write_vtu_with_pvtu_record(\n            \"\",\n            name + \"-levelsets\",\n            cycle,\n            mpi_ensemble_.ensemble_communicator(),\n            6);\n      }\n    }\n\n    /* Explicitly delete pointer to free up memory early: */\n    data_out.reset();\n  }\n\n} /* namespace ryujin */\n"
  },
  {
    "path": "tests/CMakeLists.txt",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2020 - 2025 by the ryujin authors\n##\n\nfind_program(NUMDIFF_EXECUTABLE\n  NAMES numdiff\n  HINTS ${NUMDIFF_DIR}\n  PATH_SUFFIXES bin\n  )\nmark_as_advanced(NUMDIFF_EXECUTABLE)\n\nif(\"${NUMDIFF_EXECUTABLE}\" MATCHES \"NUMDIFF_EXECUTABLE-NOTFOUND\")\n  message(STATUS \"Could not find numdiff executable. Disabling tests.\")\n\nelse()\n\n  file(GLOB _files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} CONFIGURE_DEPENDS */CMakeLists.txt)\n  foreach(_file ${_files})\n    get_filename_component(_directory \"${_file}\" DIRECTORY)\n    add_subdirectory(\"${_directory}\")\n  endforeach()\nendif()\n\nconfigure_file(${CMAKE_CURRENT_SOURCE_DIR}/scripts/print_mass ${CMAKE_CURRENT_BINARY_DIR} COPYONLY)\n"
  },
  {
    "path": "tests/Makefile",
    "content": "default: all\n.PHONY: default\n\n%:\n\t@cd .. && make $@\n.PHONY: %\n"
  },
  {
    "path": "tests/common/CMakeLists.txt",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2022 by the ryujin authors\n##\n\ninclude_directories(\n  ${CMAKE_BINARY_DIR}/source/\n  )\n\nset(TEST_LIBRARIES obj_common)\ndeal_ii_pickup_tests()\n"
  },
  {
    "path": "tests/common/fast_pow.cc",
    "content": "#include <simd.h>\n\n#include <iomanip>\n#include <iostream>\n\nint main()\n{\n  std::cout << std::setprecision(16);\n  std::cout << std::scientific;\n\n  using VA = dealii::VectorizedArray<double>;\n\n  auto test = [&](const VA a, const VA b) {\n    std::cout << \"a:        \" << a << \"\\n\";\n    std::cout << \"b:        \" << b << \"\\n\";\n    std::cout << \"pow:      \" << ryujin::pow(a, b) << \"\\n\";\n    std::cout << \"fast_pow: \" << ryujin::fast_pow(a, b, ryujin::Bias::none)\n              << \"\\n\"\n              << std::endl;\n  };\n\n  test(1.225, 2.3559);\n  test(2.135, 1. / 3.);\n}\n"
  },
  {
    "path": "tests/common/fast_pow.threads=2.output",
    "content": "a:        1.2250000000000001e+00\nb:        2.3559000000000001e+00\npow:      1.6130202194506706e+00\nfast_pow: 1.6126780509948730e+00\n\na:        2.1349999999999998e+00\nb:        3.3333333333333331e-01\npow:      1.2876543315797804e+00\nfast_pow: 1.2876543998718262e+00\n\n"
  },
  {
    "path": "tests/common/fast_pow.threads=2.output.avx2",
    "content": "a:        1.2250000000000001e+00 1.2250000000000001e+00 1.2250000000000001e+00 1.2250000000000001e+00\nb:        2.3559000000000001e+00 2.3559000000000001e+00 2.3559000000000001e+00 2.3559000000000001e+00\npow:      1.6130202194506706e+00 1.6130202194506706e+00 1.6130202194506706e+00 1.6130202194506706e+00\nfast_pow: 1.6126780509948730e+00 1.6126780509948730e+00 1.6126780509948730e+00 1.6126780509948730e+00\n\na:        2.1349999999999998e+00 2.1349999999999998e+00 2.1349999999999998e+00 2.1349999999999998e+00\nb:        3.3333333333333331e-01 3.3333333333333331e-01 3.3333333333333331e-01 3.3333333333333331e-01\npow:      1.2876543315797804e+00 1.2876543315797804e+00 1.2876543315797804e+00 1.2876543315797804e+00\nfast_pow: 1.2876543998718262e+00 1.2876543998718262e+00 1.2876543998718262e+00 1.2876543998718262e+00\n\n"
  },
  {
    "path": "tests/common/fast_pow.threads=2.output.avx512",
    "content": "a:        1.2250000000000001e+00 1.2250000000000001e+00 1.2250000000000001e+00 1.2250000000000001e+00 1.2250000000000001e+00 1.2250000000000001e+00 1.2250000000000001e+00 1.2250000000000001e+00\nb:        2.3559000000000001e+00 2.3559000000000001e+00 2.3559000000000001e+00 2.3559000000000001e+00 2.3559000000000001e+00 2.3559000000000001e+00 2.3559000000000001e+00 2.3559000000000001e+00\npow:      1.6130202194506706e+00 1.6130202194506706e+00 1.6130202194506706e+00 1.6130202194506706e+00 1.6130202194506706e+00 1.6130202194506706e+00 1.6130202194506706e+00 1.6130202194506706e+00\nfast_pow: 1.6126780509948730e+00 1.6126780509948730e+00 1.6126780509948730e+00 1.6126780509948730e+00 1.6126780509948730e+00 1.6126780509948730e+00 1.6126780509948730e+00 1.6126780509948730e+00\n\na:        2.1349999999999998e+00 2.1349999999999998e+00 2.1349999999999998e+00 2.1349999999999998e+00 2.1349999999999998e+00 2.1349999999999998e+00 2.1349999999999998e+00 2.1349999999999998e+00\nb:        3.3333333333333331e-01 3.3333333333333331e-01 3.3333333333333331e-01 3.3333333333333331e-01 3.3333333333333331e-01 3.3333333333333331e-01 3.3333333333333331e-01 3.3333333333333331e-01\npow:      1.2876543315797804e+00 1.2876543315797804e+00 1.2876543315797804e+00 1.2876543315797804e+00 1.2876543315797804e+00 1.2876543315797804e+00 1.2876543315797804e+00 1.2876543315797804e+00\nfast_pow: 1.2876543998718262e+00 1.2876543998718262e+00 1.2876543998718262e+00 1.2876543998718262e+00 1.2876543998718262e+00 1.2876543998718262e+00 1.2876543998718262e+00 1.2876543998718262e+00\n\n"
  },
  {
    "path": "tests/common/fast_pow.threads=2.output.osx-m1",
    "content": "a:        1.2250000000000001e+00\nb:        2.3559000000000001e+00\npow:      1.6130202194506706e+00\nfast_pow: 1.6130203008651733e+00\n\na:        2.1349999999999998e+00\nb:        3.3333333333333331e-01\npow:      1.2876543315797802e+00\nfast_pow: 1.2876542806625366e+00\n\n"
  },
  {
    "path": "tests/common/fast_pow.threads=2.output.osx-m1-neon",
    "content": "a:        1.2250000000000001e+00 1.2250000000000001e+00\nb:        2.3559000000000001e+00 2.3559000000000001e+00\npow:      1.6130202194506706e+00 1.6130202194506706e+00\nfast_pow: 1.6130202194506706e+00 1.6130202194506706e+00\n\na:        2.1349999999999998e+00 2.1349999999999998e+00\nb:        3.3333333333333331e-01 3.3333333333333331e-01\npow:      1.2876543315797802e+00 1.2876543315797802e+00\nfast_pow: 1.2876543315797802e+00 1.2876543315797802e+00\n\n"
  },
  {
    "path": "tests/common/fast_pow.threads=2.output.sse2",
    "content": "a:        1.2250000000000001e+00 1.2250000000000001e+00\nb:        2.3559000000000001e+00 2.3559000000000001e+00\npow:      1.6130202194506706e+00 1.6130202194506706e+00\nfast_pow: 1.6126780509948730e+00 1.6126780509948730e+00\n\na:        2.1349999999999998e+00 2.1349999999999998e+00\nb:        3.3333333333333331e-01 3.3333333333333331e-01\npow:      1.2876543315797804e+00 1.2876543315797804e+00\nfast_pow: 1.2876543998718262e+00 1.2876543998718262e+00\n\n"
  },
  {
    "path": "tests/common/multicomponent_vector_01.cc",
    "content": "#include <multicomponent_vector.h>\n\nint main(int argc, char *argv[])\n{\n  dealii::Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv);\n\n  /* Set up locally owned and relevant index sets. */\n\n  dealii::IndexSet locally_owned(12);\n  dealii::IndexSet locally_relevant(12);\n  locally_owned.add_range(0, 12);\n  locally_relevant.add_range(0, 12);\n\n  const auto scalar_partitioner =\n      std::make_shared<dealii::Utilities::MPI::Partitioner>(\n          locally_owned, locally_relevant, MPI_COMM_WORLD);\n\n  const auto vector_partitioner =\n      ryujin::Vectors::create_vector_partitioner(scalar_partitioner, 4);\n\n  ryujin::Vectors::MultiComponentVector<double, 1> scalar_vector;\n\n  scalar_vector.reinit_with_scalar_partitioner(scalar_partitioner);\n\n  ryujin::Vectors::MultiComponentVector<double, 4> state_vector;\n\n  state_vector.reinit_with_vector_partitioner(vector_partitioner);\n\n  for (unsigned int i = 0; i < 12; ++i) {\n    scalar_vector.write_entry<double>(static_cast<double>(i), i);\n\n    dealii::Tensor<1, 4, double> tensor{{static_cast<double>(10 * i),\n                                         static_cast<double>(10 * i + 1),\n                                         static_cast<double>(10 * i + 2),\n                                         static_cast<double>(10 * i + 3)}};\n    state_vector.write_tensor<double>(tensor, i);\n  }\n\n  std::cout << \"Scalar vector:\\n\";\n  for (unsigned int i = 0; i < 12; ++i) {\n    std::cout << scalar_vector.read_entry<double>(i) << \"\\n\";\n  }\n\n  std::cout << \"\\nState vector:\\n\";\n  for (unsigned int i = 0; i < 12; ++i) {\n    std::cout << state_vector.read_tensor<double>(i) << \"\\n\";\n  }\n}\n"
  },
  {
    "path": "tests/common/multicomponent_vector_01.threads=2.output",
    "content": "Scalar vector:\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n\nState vector:\n0 1 2 3\n10 11 12 13\n20 21 22 23\n30 31 32 33\n40 41 42 43\n50 51 52 53\n60 61 62 63\n70 71 72 73\n80 81 82 83\n90 91 92 93\n100 101 102 103\n110 111 112 113\n"
  },
  {
    "path": "tests/common/multicomponent_vector_02.cc",
    "content": "#include <multicomponent_vector.h>\n\nint main(int argc, char *argv[])\n{\n  dealii::Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv);\n\n  /* Set up locally owned and relevant index sets. */\n\n  dealii::IndexSet locally_owned(12);\n  dealii::IndexSet locally_relevant(12);\n  locally_owned.add_range(0, 12);\n  locally_relevant.add_range(0, 12);\n\n  const auto scalar_partitioner =\n      std::make_shared<dealii::Utilities::MPI::Partitioner>(\n          locally_owned, locally_relevant, MPI_COMM_WORLD);\n\n  const auto vector_partitioner =\n      ryujin::Vectors::create_vector_partitioner(scalar_partitioner, 4);\n\n  ryujin::Vectors::MultiComponentVector<double, 1> scalar_vector;\n\n  scalar_vector.reinit_with_scalar_partitioner(scalar_partitioner);\n\n  ryujin::Vectors::MultiComponentVector<double, 4> state_vector;\n\n  state_vector.reinit_with_vector_partitioner(vector_partitioner);\n\n  for (unsigned int i = 0; i < 12; ++i) {\n    scalar_vector.write_entry<double>(static_cast<double>(i), i);\n\n    dealii::Tensor<1, 4, double> tensor{{static_cast<double>(10 * i),\n                                         static_cast<double>(10 * i + 1),\n                                         static_cast<double>(10 * i + 2),\n                                         static_cast<double>(10 * i + 3)}};\n    state_vector.write_tensor<double>(tensor, i);\n  }\n\n  using VA = dealii::VectorizedArray<double>;\n  constexpr auto simd_width = VA::size();\n\n  std::cout << \"Scalar vector (packed SIMD)\\n\";\n  for (unsigned int i = 0; i < 8; i += simd_width) {\n    std::cout << scalar_vector.read_entry<VA>(i) << \"\\n\";\n  }\n\n  std::cout << \"\\nState vector (packed SIMD):\\n\";\n  for (unsigned int i = 0; i < 8; i += simd_width) {\n    std::cout << state_vector.read_tensor<VA>(i) << \"\\n\";\n  }\n}\n"
  },
  {
    "path": "tests/common/multicomponent_vector_02.threads=2.output",
    "content": "Scalar vector:\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n\nState vector:\n0 1 2 3\n10 11 12 13\n20 21 22 23\n30 31 32 33\n40 41 42 43\n50 51 52 53\n60 61 62 63\n70 71 72 73\n80 81 82 83\n90 91 92 93\n100 101 102 103\n110 111 112 113\n"
  },
  {
    "path": "tests/common/multicomponent_vector_02.threads=2.output.avx2",
    "content": "Scalar vector (packed SIMD)\n0 1 2 3\n4 5 6 7\n\nState vector (packed SIMD):\n0 10 20 30 1 11 21 31 2 12 22 32 3 13 23 33\n40 50 60 70 41 51 61 71 42 52 62 72 43 53 63 73\n"
  },
  {
    "path": "tests/common/multicomponent_vector_02.threads=2.output.avx512",
    "content": "Scalar vector (packed SIMD)\n0 1 2 3 4 5 6 7\n\nState vector (packed SIMD):\n0 10 20 30 40 50 60 70 1 11 21 31 41 51 61 71 2 12 22 32 42 52 62 72 3 13 23 33 43 53 63 73\n"
  },
  {
    "path": "tests/common/multicomponent_vector_02.threads=2.output.sse2",
    "content": "Scalar vector (packed SIMD)\n0 1\n2 3\n4 5\n6 7\n\nState vector (packed SIMD):\n0 10 1 11 2 12 3 13\n20 30 21 31 22 32 23 33\n40 50 41 51 42 52 43 53\n60 70 61 71 62 72 63 73\n"
  },
  {
    "path": "tests/common/multicomponent_vector_03.cc",
    "content": "#include <multicomponent_vector.h>\n\nint main(int argc, char *argv[])\n{\n  dealii::Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv);\n\n  /* Set up locally owned and relevant index sets. */\n\n  dealii::IndexSet locally_owned(12);\n  dealii::IndexSet locally_relevant(12);\n  locally_owned.add_range(0, 12);\n  locally_relevant.add_range(0, 12);\n\n  const auto scalar_partitioner =\n      std::make_shared<dealii::Utilities::MPI::Partitioner>(\n          locally_owned, locally_relevant, MPI_COMM_WORLD);\n\n  const auto vector_partitioner =\n      ryujin::Vectors::create_vector_partitioner(scalar_partitioner, 4);\n\n  ryujin::Vectors::MultiComponentVector<double, 4> state_vector;\n\n  state_vector.reinit_with_vector_partitioner(vector_partitioner);\n\n  using HostSpace = dealii::MemorySpace::Host;\n  using DefaultSpace = dealii::MemorySpace::Default;\n\n  const auto print_status = [&]() {\n    std::cout << \"HostSpace active == \"\n              << state_vector.is_active_memory_space<HostSpace>() << std::endl\n              << \"DefaultSpace active == \"\n              << state_vector.is_active_memory_space<DefaultSpace>()\n              << std::endl;\n  };\n\n  print_status();\n\n  for (unsigned int i = 0; i < 12; ++i) {\n    dealii::Tensor<1, 4, double> tensor{{static_cast<double>(10 * i),\n                                         static_cast<double>(10 * i + 1),\n                                         static_cast<double>(10 * i + 2),\n                                         static_cast<double>(10 * i + 3)}};\n    state_vector.write_tensor<double>(tensor, i);\n  }\n\n  std::cout << \"\\nState vector:\\n\";\n  for (unsigned int i = 0; i < 8; i += 1) {\n    std::cout << state_vector.read_tensor<double>(i) << \"\\n\";\n  }\n\n  std::cout << \"After move to DefaultSpace:\" << std::endl;\n  state_vector.move_to_memory_space<DefaultSpace>();\n  print_status();\n  std::cout << \"After repeated move to DefaultSpace:\" << std::endl;\n  state_vector.move_to_memory_space<DefaultSpace>();\n  print_status();\n\n  const auto &view = state_vector.template get_view<DefaultSpace>();\n  using ExecutionSpace = DefaultSpace::kokkos_space::execution_space;\n  const auto exec = ExecutionSpace{};\n  Kokkos::parallel_for(\"test\",\n                       Kokkos::RangePolicy<ExecutionSpace>(exec, 0, 3),\n                       [=](std::size_t i) {\n                         auto a = view.read_tensor(i);\n                         a *= 10.;\n                         view.write_tensor(a, i);\n                       });\n\n  std::cout << \"After move to HostSpace:\" << std::endl;\n  state_vector.move_to_memory_space<HostSpace>();\n  print_status();\n\n  std::cout << \"\\nState vector:\\n\";\n  for (unsigned int i = 0; i < 8; i += 1) {\n    std::cout << state_vector.read_tensor<double>(i) << \"\\n\";\n  }\n}\n"
  },
  {
    "path": "tests/common/multicomponent_vector_03.threads=2.output",
    "content": "HostSpace active == 1\nDefaultSpace active == 0\n\nState vector:\n0 1 2 3\n10 11 12 13\n20 21 22 23\n30 31 32 33\n40 41 42 43\n50 51 52 53\n60 61 62 63\n70 71 72 73\nAfter move to DefaultSpace:\nHostSpace active == 0\nDefaultSpace active == 1\nAfter repeated move to DefaultSpace:\nHostSpace active == 0\nDefaultSpace active == 1\nAfter move to HostSpace:\nHostSpace active == 1\nDefaultSpace active == 0\n\nState vector:\n0 10 20 30\n100 110 120 130\n200 210 220 230\n30 31 32 33\n40 41 42 43\n50 51 52 53\n60 61 62 63\n70 71 72 73\n"
  },
  {
    "path": "tests/common/sparse_matrix_01.cc",
    "content": "#include <sparse_matrix.h>\n\nint main(int argc, char *argv[])\n{\n  //\n  // Test basic SIMD functionality:\n  //\n\n  dealii::Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv);\n\n  using VA = dealii::VectorizedArray<double>;\n  constexpr auto simd_width = VA::size();\n\n  dealii::DynamicSparsityPattern spars(14, 14);\n  spars.add(0, 0);\n  spars.add(0, 1);\n  spars.add(0, 13);\n  for (unsigned int i = 1; i < 12; ++i) {\n    spars.add(i, i - 1);\n    spars.add(i, i);\n    spars.add(i, i + 1);\n  }\n  spars.add(12, 12);\n  spars.add(12, 11);\n  spars.add(13, 13);\n  spars.add(13, 0);\n  spars.compress();\n\n  dealii::IndexSet locally_owned(14);\n  locally_owned.add_range(0, 14);\n  dealii::IndexSet locally_relevant(14);\n  auto partitioner = std::make_shared<dealii::Utilities::MPI::Partitioner>(\n      locally_owned, locally_relevant, MPI_COMM_SELF);\n\n  ryujin::SparsityPattern<simd_width> my_sparsity(\n      (12 / simd_width) * simd_width, spars, partitioner);\n\n  ryujin::SparseMatrix<double, 1, simd_width> my_sparse(my_sparsity);\n  for (unsigned i = 0; i < 12; ++i)\n    for (unsigned j = 0; j < 3; ++j)\n      my_sparse.write_entry(static_cast<double>(i * 3 + j), i, j);\n  my_sparse.write_entry(36., 12, 0);\n  my_sparse.write_entry(37., 12, 1);\n  my_sparse.write_entry(38., 13, 0);\n  my_sparse.write_entry(39., 13, 1);\n  std::cout << \"Matrix entries row by row\" << std::endl;\n  for (unsigned int i = 0; i < my_sparsity.n_rows(); ++i) {\n    for (unsigned int j = 0; j < my_sparsity.row_length(i); ++j) {\n      const auto a = my_sparse.read_entry(i, j);\n      std::cout << a << \" \";\n    }\n    std::cout << std::endl;\n  }\n  std::cout << \"Matrix entries by SIMD rows\" << std::endl;\n  unsigned int i = 0;\n  for (; i < (12 / simd_width) * simd_width; i += simd_width) {\n    for (unsigned int j = 0; j < 3; ++j) {\n      const auto a = my_sparse.template read_entry<VA>(i, j);\n      std::cout << a << \"   \";\n    }\n    std::cout << std::endl;\n  }\n  for (; i < 14; i++)\n    std::cout << my_sparse.read_entry(i, 0) << \" \" << my_sparse.read_entry(i, 1)\n              << \" \";\n  std::cout << std::endl;\n\n  std::cout << \"Matrix entries transposed row by row\" << std::endl;\n  for (unsigned int i = 0; i < my_sparsity.n_rows(); ++i) {\n    for (unsigned int j = 0; j < my_sparsity.row_length(i); ++j) {\n      const auto a = my_sparse.read_transposed_entry(i, j);\n      std::cout << a << \" \";\n    }\n    std::cout << std::endl;\n  }\n\n  std::cout << \"Matrix entries transposed by SIMD row\" << std::endl;\n  i = 0;\n  for (; i < (12 / simd_width) * simd_width; i += simd_width) {\n    for (unsigned int j = 0; j < 3; ++j) {\n      const auto a = my_sparse.template read_transposed_entry<VA>(i, j);\n      std::cout << a << \"   \";\n    }\n    std::cout << std::endl;\n  }\n  for (; i < 14; i++)\n    std::cout << my_sparse.read_transposed_entry(i, 0) << \" \"\n              << my_sparse.read_transposed_entry(i, 1) << \" \";\n  std::cout << std::endl;\n}\n"
  },
  {
    "path": "tests/common/sparse_matrix_01.threads=2.output",
    "content": "Matrix entries row by row\n0 1 2 \n3 4 5 \n6 7 8 \n9 10 11 \n12 13 14 \n15 16 17 \n18 19 20 \n21 22 23 \n24 25 26 \n27 28 29 \n30 31 32 \n33 34 35 \n36 37 \n38 39 \nMatrix entries by SIMD rows\n0   1   2   \n3   4   5   \n6   7   8   \n9   10   11   \n12   13   14   \n15   16   17   \n18   19   20   \n21   22   23   \n24   25   26   \n27   28   29   \n30   31   32   \n33   34   35   \n36 37 38 39\nMatrix entries transposed row by row\n0 4 39 \n3 1 7 \n6 5 10 \n9 8 13 \n12 11 16 \n15 14 19 \n18 17 22 \n21 20 25 \n24 23 28 \n27 26 31 \n30 29 34 \n33 32 37 \n36 35 \n38 2 \nMatrix entries transposed by SIMD row\n0   4   39   \n3   1   7   \n6   5   10   \n9   8   13   \n12   11   16   \n15   14   19   \n18   17   22   \n21   20   25   \n24   23   28   \n27   26   31   \n30   29   34   \n33   32   37   \n36 35 38 2\n"
  },
  {
    "path": "tests/common/sparse_matrix_01.threads=2.output.avx2",
    "content": "Matrix entries row by row\n0 1 2 \n3 4 5 \n6 7 8 \n9 10 11 \n12 13 14 \n15 16 17 \n18 19 20 \n21 22 23 \n24 25 26 \n27 28 29 \n30 31 32 \n33 34 35 \n36 37 \n38 39 \nMatrix entries by SIMD rows\n0 3 6 9   1 4 7 10   2 5 8 11   \n12 15 18 21   13 16 19 22   14 17 20 23   \n24 27 30 33   25 28 31 34   26 29 32 35   \n36 37 38 39\nMatrix entries transposed row by row\n0 4 39 \n3 1 7 \n6 5 10 \n9 8 13 \n12 11 16 \n15 14 19 \n18 17 22 \n21 20 25 \n24 23 28 \n27 26 31 \n30 29 34 \n33 32 37 \n36 35 \n38 2 \nMatrix entries transposed by SIMD row\n0 3 6 9   4 1 5 8   39 7 10 13   \n12 15 18 21   11 14 17 20   16 19 22 25   \n24 27 30 33   23 26 29 32   28 31 34 37   \n36 35 38 2\n"
  },
  {
    "path": "tests/common/sparse_matrix_01.threads=2.output.avx512",
    "content": "Matrix entries row by row\n0 1 2 \n3 4 5 \n6 7 8 \n9 10 11 \n12 13 14 \n15 16 17 \n18 19 20 \n21 22 23 \n24 25 26 \n27 28 29 \n30 31 32 \n33 34 35 \n36 37 \n38 39 \nMatrix entries by SIMD rows\n0 3 6 9 12 15 18 21   1 4 7 10 13 16 19 22   2 5 8 11 14 17 20 23   \n24 25 27 28 30 31 33 34 36 37 38 39 \nMatrix entries transposed row by row\n0 4 39 \n3 1 7 \n6 5 10 \n9 8 13 \n12 11 16 \n15 14 19 \n18 17 22 \n21 20 25 \n24 23 28 \n27 26 31 \n30 29 34 \n33 32 37 \n36 35 \n38 2 \nMatrix entries transposed by SIMD row\n0 3 6 9 12 15 18 21   4 1 5 8 11 14 17 20   39 7 10 13 16 19 22 25   \n24 23 27 26 30 29 33 32 36 35 38 2 \n"
  },
  {
    "path": "tests/common/sparse_matrix_01.threads=2.output.sse2",
    "content": "Matrix entries row by row\n0 1 2 \n3 4 5 \n6 7 8 \n9 10 11 \n12 13 14 \n15 16 17 \n18 19 20 \n21 22 23 \n24 25 26 \n27 28 29 \n30 31 32 \n33 34 35 \n36 37 \n38 39 \nMatrix entries by SIMD rows\n0 3   1 4   2 5   \n6 9   7 10   8 11   \n12 15   13 16   14 17   \n18 21   19 22   20 23   \n24 27   25 28   26 29   \n30 33   31 34   32 35   \n36 37 38 39 \nMatrix entries transposed row by row\n0 4 39 \n3 1 7 \n6 5 10 \n9 8 13 \n12 11 16 \n15 14 19 \n18 17 22 \n21 20 25 \n24 23 28 \n27 26 31 \n30 29 34 \n33 32 37 \n36 35 \n38 2 \nMatrix entries transposed by SIMD row\n0 3   4 1   39 7   \n6 9   5 8   10 13   \n12 15   11 14   16 19   \n18 21   17 20   22 25   \n24 27   23 26   28 31   \n30 33   29 32   34 37   \n36 35 38 2 \n"
  },
  {
    "path": "tests/common/sparse_matrix_02.cc",
    "content": "#include <sparse_matrix.h>\n\nint main(int argc, char *argv[])\n{\n  //\n  // Test memory space transfer:\n  //\n\n  dealii::Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv);\n\n  using VA = dealii::VectorizedArray<double>;\n  constexpr auto simd_width = VA::size();\n\n  /* Create sparsity pattern: */\n\n  dealii::DynamicSparsityPattern dsp(3, 3);\n  dsp.add(0, 0);\n  dsp.add(0, 1);\n  dsp.add(0, 2);\n  dsp.add(1, 0);\n  dsp.add(1, 1);\n  dsp.add(1, 2);\n  dsp.add(2, 0);\n  dsp.add(2, 1);\n  dsp.add(2, 2);\n  dsp.compress();\n\n  dealii::IndexSet locally_owned(3);\n  locally_owned.add_range(0, 3);\n  dealii::IndexSet locally_relevant(3);\n  auto partitioner = std::make_shared<dealii::Utilities::MPI::Partitioner>(\n      locally_owned, locally_relevant, MPI_COMM_SELF);\n\n  ryujin::SparsityPattern<simd_width> sparsity_pattern(0, dsp, partitioner);\n\n  ryujin::SparseMatrix<double, 1, simd_width> sparse_matrix;\n  sparse_matrix.reinit(sparsity_pattern);\n\n  using HostSpace = dealii::MemorySpace::Host;\n  using DefaultSpace = dealii::MemorySpace::Default;\n\n  const auto print_status = [&]() {\n    std::cout << \"HostSpace active == \"\n              << sparse_matrix.is_active_memory_space<HostSpace>() << std::endl\n              << \"DefaultSpace active == \"\n              << sparse_matrix.is_active_memory_space<DefaultSpace>()\n              << std::endl;\n  };\n\n  /* Fill entries on the host space: */\n\n  print_status();\n  sparse_matrix.write_entry(22.0, 0, 1);\n  sparse_matrix.write_entry(20.0, 0, 2);\n  sparse_matrix.write_entry(220.0, 1, 1);\n  sparse_matrix.write_entry(200.0, 1, 2);\n  sparse_matrix.write_entry(2200.0, 2, 1);\n  sparse_matrix.write_entry(2000.0, 2, 2);\n\n  /* Sum up rows on the default space: */\n\n  std::cout << \"After move to DefaultSpace:\" << std::endl;\n  sparse_matrix.move_to_memory_space<DefaultSpace>();\n  print_status();\n  std::cout << \"After repeated move to DefaultSpace:\" << std::endl;\n  sparse_matrix.move_to_memory_space<DefaultSpace>();\n  print_status();\n\n  const auto &view = sparse_matrix.template get_view<DefaultSpace>();\n  using ExecutionSpace = DefaultSpace::kokkos_space::execution_space;\n  const auto exec = ExecutionSpace{};\n  Kokkos::parallel_for(\"test\",\n                       Kokkos::RangePolicy<ExecutionSpace>(exec, 0, 3),\n                       [=](std::size_t i) {\n                         const auto a = view.read_entry(i, 1);\n                         const auto b = view.read_entry(i, 2);\n                         view.write_entry(a + b, i, 0);\n                       });\n\n\n  /* Read entries on the host space: */\n\n  std::cout << \"After move to HostSpace:\" << std::endl;\n  sparse_matrix.move_to_memory_space<HostSpace>();\n  print_status();\n\n  std::cout << \"Entry (0, 0): \" << sparse_matrix.read_entry(0, 0) << std::endl;\n  std::cout << \"Entry (1, 1): \" << sparse_matrix.read_entry(1, 0) << std::endl;\n  std::cout << \"Entry (2, 2): \" << sparse_matrix.read_entry(2, 0) << std::endl;\n}\n"
  },
  {
    "path": "tests/common/sparse_matrix_02.threads=2.output",
    "content": "HostSpace active == 1\nDefaultSpace active == 0\nAfter move to DefaultSpace:\nHostSpace active == 0\nDefaultSpace active == 1\nAfter repeated move to DefaultSpace:\nHostSpace active == 0\nDefaultSpace active == 1\nAfter move to HostSpace:\nHostSpace active == 1\nDefaultSpace active == 0\nEntry (0, 0): 42\nEntry (1, 1): 420\nEntry (2, 2): 4200\n"
  },
  {
    "path": "tests/common/sparse_matrix_03.cc",
    "content": "#include <sparse_matrix.h>\n\nint main(int argc, char *argv[])\n{\n  //\n  // Test creation of SparsityPatternView from mutable and const SparseMatrix:\n  //\n\n  dealii::Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv);\n\n  using VA = dealii::VectorizedArray<double>;\n  constexpr auto simd_width = VA::size();\n\n  /* Create sparsity pattern: */\n\n  dealii::DynamicSparsityPattern dsp(1, 1);\n  dsp.add(0, 0);\n  dsp.compress();\n\n  dealii::IndexSet locally_owned(1);\n  locally_owned.add_range(0, 1);\n  dealii::IndexSet locally_relevant(1);\n  auto partitioner = std::make_shared<dealii::Utilities::MPI::Partitioner>(\n      locally_owned, locally_relevant, MPI_COMM_SELF);\n\n  ryujin::SparsityPattern<simd_width> sparsity_pattern(0, dsp, partitioner);\n\n  ryujin::SparseMatrix<double, 1, simd_width> sparse_matrix;\n  sparse_matrix.reinit(sparsity_pattern);\n\n\n  auto view_1 = sparse_matrix.get_view();\n  const auto view_2 = sparse_matrix.get_view();\n  std::cout << view_1.read_entry(0, 0) << std::endl;\n  std::cout << view_2.read_entry(0, 0) << std::endl;\n  view_1.write_entry(0., 0, 0); // OK\n  view_2.write_entry(1., 0, 0); // OK\n  view_1.add_entry(2., 0, 0);   // OK\n  view_2.add_entry(3., 0, 0);   // OK\n  std::cout << view_1.read_entry(0, 0) << std::endl;\n  std::cout << view_2.read_entry(0, 0) << std::endl;\n\n  auto &sparse_matrix_ref_1 = sparse_matrix;\n\n  auto view_3 = sparse_matrix_ref_1.get_view();\n  const auto view_4 = sparse_matrix_ref_1.get_view();\n\n  std::cout << view_3.read_entry(0, 0) << std::endl;\n  std::cout << view_4.read_entry(0, 0) << std::endl;\n  view_3.write_entry(10., 0, 0); // OK\n  view_4.write_entry(11., 0, 0); // OK\n  view_3.add_entry(2., 0, 0);    // OK\n  view_4.add_entry(3., 0, 0);    // OK\n  std::cout << view_3.read_entry(0, 0) << std::endl;\n  std::cout << view_4.read_entry(0, 0) << std::endl;\n\n  const auto &sparse_matrix_ref_2 = sparse_matrix;\n\n  auto view_5 = sparse_matrix_ref_2.get_view();\n  const auto view_6 = sparse_matrix_ref_2.get_view();\n\n  std::cout << view_5.read_entry(0, 0) << std::endl;\n  std::cout << view_6.read_entry(0, 0) << std::endl;\n  // view_5.write_entry(0., 0, 0); // disallowed due to writable == false\n  // view_6.write_entry(1., 0, 0); // disallowed due to writable == false\n  // view_5.add_entry(2., 0, 0);   // disallowed due to writable == false\n  // view_6.add_entry(3., 0, 0);   // disallowed due to writable == false\n}\n"
  },
  {
    "path": "tests/common/sparse_matrix_03.threads=2.output",
    "content": "0\n0\n6\n6\n6\n6\n16\n16\n16\n16\n"
  },
  {
    "path": "tests/common/sparse_matrix_04.cc",
    "content": "#include <sparse_matrix.h>\n#include <sparsity_pattern.h>\n\n#include <deal.II/distributed/tria.h>\n#include <deal.II/dofs/dof_handler.h>\n#include <deal.II/dofs/dof_tools.h>\n#include <deal.II/fe/fe_q.h>\n#include <deal.II/grid/grid_generator.h>\n#include <deal.II/grid/grid_out.h>\n#include <deal.II/lac/sparsity_tools.h>\n\n#include <deal.II/lac/trilinos_sparse_matrix.h>\n\n#include <chrono>\n\n/*\n * Test SparseMatrix::compress(VectorOperation::add)\n */\n\nnamespace ryujin\n{\n  template <int simd_length>\n  class Debug : public SparsityPattern<simd_length>\n  {\n  public:\n    Debug(const unsigned int n_internal_dofs,\n          const dealii::DynamicSparsityPattern &sparsity,\n          const std::shared_ptr<const dealii::Utilities::MPI::Partitioner>\n              &partitioner)\n        : SparsityPattern<simd_length>(n_internal_dofs,\n                                       sparsity,\n                                       partitioner,\n                                       /*symmetrize ghost range*/ false)\n    {\n    }\n\n    void print()\n    {\n      std::stringstream ss;\n\n      ss << \"Receive targets:\\n\";\n      for (const auto &[left, right] : this->receive_targets())\n        ss << left << \" : \" << right << \"\\n\";\n\n      ss << \"Send targets:\\n\";\n      for (const auto &[left, right] : this->send_targets())\n        ss << left << \" : \" << right << \"\\n\";\n\n      ss << \"Entries to be sent:\\n\";\n      for (const auto &[left, right] : this->entries_to_be_sent())\n        ss << left << \" : \" << right << \"\\n\";\n\n      std::cout << ss.str() << std::endl;\n    }\n  };\n} // namespace ryujin\n\n\nint main(int argc, char *argv[])\n{\n  using namespace std::chrono_literals;\n\n  dealii::Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv);\n\n  const auto mpi_rank =\n      dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD);\n\n  const auto n_mpi_processes =\n      dealii::Utilities::MPI::n_mpi_processes(MPI_COMM_WORLD);\n\n  /*\n   * Create a unit square twice globally refined and the bottom left\n   * quadrant once more. Creating a mesh with 4 hanging nodes.\n   */\n\n  constexpr int dim = 2;\n\n  dealii::parallel::distributed::Triangulation<dim> triangulation(\n      MPI_COMM_WORLD);\n  dealii::GridGenerator::hyper_cube(triangulation);\n  triangulation.refine_global(1);\n  triangulation.begin_active()->set_refine_flag();\n  triangulation.execute_coarsening_and_refinement();\n  triangulation.refine_global(1);\n\n  dealii::FE_Q<dim> fe(1);\n\n  /*\n   * Distribute DoFs, set up locally owned and relevant ranges, and partitioner.\n   */\n\n  dealii::DoFHandler<dim> dof_handler(triangulation);\n  dof_handler.distribute_dofs(fe);\n\n  const dealii::IndexSet &locally_owned = dof_handler.locally_owned_dofs();\n\n  auto locally_relevant =\n      dealii::DoFTools::extract_locally_relevant_dofs(dof_handler);\n\n  dealii::AffineConstraints<double> affine_constraints;\n  affine_constraints.reinit(locally_owned, locally_relevant);\n  dealii::DoFTools::make_hanging_node_constraints(dof_handler,\n                                                  affine_constraints);\n  affine_constraints.close();\n\n  /* We should be consistent... */\n  if (!affine_constraints.is_consistent_in_parallel(\n          dealii::Utilities::MPI::all_gather(MPI_COMM_WORLD, locally_owned),\n          locally_relevant,\n          MPI_COMM_WORLD,\n          true)) {\n    std::cout << \"Oh Nooo!\" << std::endl;\n    __builtin_trap();\n  }\n\n  dealii::DynamicSparsityPattern dsp;\n  dsp.reinit(dof_handler.n_dofs(), dof_handler.n_dofs(), locally_relevant);\n\n  /* Create temporary sparsity pattern: */\n  dealii::DoFTools::make_sparsity_pattern(\n      dof_handler, dsp, affine_constraints, false);\n  dealii::SparsityTools::distribute_sparsity_pattern(\n      dsp, locally_owned, MPI_COMM_WORLD, locally_relevant);\n\n  for (unsigned int i = 0; i < n_mpi_processes; ++i) {\n    if (i == mpi_rank) {\n      if (mpi_rank == 0)\n        std::cout << \"\\nPreliminary sparsity pattern (global numbering):\\n\";\n      std::cout << \"Rank \" << mpi_rank << std::endl;\n      dsp.print(std::cout);\n    }\n    std::this_thread::sleep_for(200ms);\n    MPI_Barrier(MPI_COMM_WORLD);\n  }\n\n  /*\n   * Enlarge the locally relevant set to include all additional couplings:\n   */\n\n  dealii::IndexSet additional_dofs(dof_handler.n_dofs());\n  for (auto &entry : dsp)\n    if (!locally_relevant.is_element(entry.column())) {\n      Assert(locally_owned.is_element(entry.row()), dealii::ExcInternalError());\n      additional_dofs.add_index(entry.column());\n    }\n  additional_dofs.compress();\n  locally_relevant.add_indices(additional_dofs);\n  locally_relevant.compress();\n  const auto n_locally_relevant = locally_relevant.n_elements();\n\n  for (unsigned int i = 0; i < n_mpi_processes; ++i) {\n    if (i == mpi_rank) {\n      if (mpi_rank == 0)\n        std::cout << \"\\nIndex sets (owned and extended relevant):\\n\";\n      std::cout << \"Rank \" << mpi_rank << std::endl;\n      locally_owned.print(std::cout);\n      locally_relevant.print(std::cout);\n    }\n    std::this_thread::sleep_for(200ms);\n    MPI_Barrier(MPI_COMM_WORLD);\n  }\n\n  const auto partitioner =\n      std::make_shared<dealii::Utilities::MPI::Partitioner>(\n          locally_owned, locally_relevant, MPI_COMM_WORLD);\n\n  /*\n   * Create final sparsity pattern:\n   */\n\n  using VA = dealii::VectorizedArray<double>;\n  constexpr auto simd_width = VA::size();\n  ryujin::Debug<simd_width> sparsity_pattern(0, dsp, partitioner);\n  const auto print_sparsity = [&]() {\n    for (unsigned int i = 0; i < n_locally_relevant; ++i) {\n      const auto i_global = partitioner->local_to_global(i);\n      const unsigned int row_length = sparsity_pattern.row_length(i);\n      const unsigned int *js = sparsity_pattern.columns(i);\n      std::cout << \"[\" << i_global;\n      for (unsigned int col_idx = 0; col_idx < row_length; ++col_idx, ++js) {\n        const auto j_global = partitioner->local_to_global(*js);\n        std::cout << \",\" << j_global;\n      }\n      std::cout << \"]\" << std::endl;\n    }\n  };\n\n  for (unsigned int i = 0; i < n_mpi_processes; ++i) {\n    if (i == mpi_rank) {\n      if (mpi_rank == 0)\n        std::cout << \"\\nModified sparsity pattern (global numbering):\\n\";\n      std::cout << \"Rank \" << mpi_rank << std::endl;\n      print_sparsity();\n    }\n    std::this_thread::sleep_for(200ms);\n    MPI_Barrier(MPI_COMM_WORLD);\n  }\n\n  for (unsigned int i = 0; i < n_mpi_processes; ++i) {\n    if (i == mpi_rank) {\n      if (mpi_rank == 0)\n        std::cout << \"\\nExchange pattern:\\n\";\n      std::cout << \"Rank \" << mpi_rank << std::endl;\n      sparsity_pattern.print();\n    }\n    std::this_thread::sleep_for(200ms);\n    MPI_Barrier(MPI_COMM_WORLD);\n  }\n\n  /*\n   * Create a sparse matrix:\n   */\n\n  ryujin::SparseMatrix<double, 1, simd_width> sparse_matrix;\n  sparse_matrix.reinit(sparsity_pattern);\n\n  for (unsigned int i = 0; i < n_locally_relevant; ++i) {\n    const unsigned int row_length = sparsity_pattern.row_length(i);\n    for (unsigned int col_idx = 0; col_idx < row_length; ++col_idx) {\n      sparse_matrix.write_entry(std::pow(10., mpi_rank), i, col_idx);\n    }\n  }\n\n  sparse_matrix.compress(dealii::VectorOperation::add);\n\n  const auto print_matrix = [&]() {\n    for (unsigned int i = 0; i < n_locally_relevant; ++i) {\n      const auto i_global = partitioner->local_to_global(i);\n      const unsigned int row_length = sparsity_pattern.row_length(i);\n      const unsigned int *js = sparsity_pattern.columns(i);\n      for (unsigned int col_idx = 0; col_idx < row_length; ++col_idx, ++js) {\n        const auto j_global = partitioner->local_to_global(*js);\n        std::cout << \"(\" << i_global << \",\" << j_global << \") \"\n                  << sparse_matrix.read_entry(i, col_idx) << std::endl;\n      }\n    }\n  };\n\n  for (unsigned int i = 0; i < n_mpi_processes; ++i) {\n    if (i == mpi_rank) {\n      if (mpi_rank == 0)\n        std::cout << \"\\n\\nSparse matrix contents:\\n\";\n      std::cout << \"Rank \" << mpi_rank << std::endl;\n      print_matrix();\n    }\n    std::this_thread::sleep_for(200ms);\n    MPI_Barrier(MPI_COMM_WORLD);\n  }\n\n#if 0\n  /*\n   * Reference values computed with Trilinos matrix: Note that diagonal\n   * entries might differ between the two matrices because we always store\n   * the diagonal entry in our custom sparsity pattern.\n   */\n\n  dealii::TrilinosWrappers::SparsityPattern trilinos_sparsity_pattern;\n  trilinos_sparsity_pattern.reinit(locally_owned, dsp, MPI_COMM_WORLD);\n  dealii::TrilinosWrappers::SparseMatrix trilinos_sparse_matrix(\n      trilinos_sparsity_pattern);\n\n  for (const auto &it : dsp) {\n    const auto i = it.row();\n    const auto j = it.column();\n    trilinos_sparse_matrix.add(i, j, std::pow(10., mpi_rank));\n  }\n\n  trilinos_sparse_matrix.compress(dealii::VectorOperation::add);\n\n  for (unsigned int i = 0; i < n_mpi_processes; ++i) {\n    if (i == mpi_rank) {\n      if (mpi_rank == 0)\n        std::cout << \"\\n\\n(Reference) sparse matrix contents:\\n\";\n      std::cout << \"Rank \" << mpi_rank << \"\\n\";\n      trilinos_sparse_matrix.print(std::cout);\n    }\n    MPI_Barrier(MPI_COMM_WORLD);\n  }\n#endif\n}\n"
  },
  {
    "path": "tests/common/sparse_matrix_04.mpirun=4.threads=2.output",
    "content": "\nPreliminary sparsity pattern (global numbering):\nRank 0\n[0,0,1,2,3]\n[1,0,1,2,3,4,5]\n[2,0,1,2,3,6,7]\n[3,0,1,2,3,4,5,6,7,8]\n[4,1,3,4,5,9,10]\n[5,1,3,4,5,7,8,9,10,13]\n[6,2,3,6,7,15,16]\n[7,2,3,5,6,7,8,15,16,17]\n[8,3,5,7,8,10,13,16,17,21]\n[9,4,5,9,10,11,14]\n[10,4,5,8,9,10,11,13,14]\n[11,9,10,11,13,14,25,26]\n[12,12]\n[13,5,8,10,11,13,14,17,21,24]\n[14,9,10,11,13,14,20,21,24,25,26,29]\n[15]\n[16]\n[17]\n[21]\n[22]\n[24]\n[25]\n[26]\n[29]\nRank 1\n[2]\n[3]\n[5]\n[6,6,7,15,16]\n[7,6,7,8,15,16,17]\n[8,7,8,13,16,17,21]\n[10]\n[11]\n[12]\n[13,8,13,14,17,21,24]\n[14,13,14,20,21,24]\n[15,6,7,15,16,18,20]\n[16,6,7,8,15,16,17,18,20]\n[17,7,8,13,16,17,18,20,21,24]\n[18,15,16,17,18,20,31,32]\n[19,19]\n[20,14,15,16,17,18,20,21,24,31,32,33]\n[21,8,13,14,17,20,21,24]\n[22,22]\n[23,23]\n[24,13,14,17,20,21,24,26,29,32,33,37]\n[25]\n[26]\n[29]\n[31]\n[32]\n[33]\n[37]\nRank 2\n[9]\n[10]\n[11,11,14,25,26]\n[12]\n[13]\n[14,11,14,24,25,26,29]\n[20]\n[21]\n[22]\n[23]\n[24,14,24,26,29]\n[25,11,14,25,26,27,28]\n[26,11,14,24,25,26,27,28,29,30]\n[27,25,26,27,28]\n[28,25,26,27,28,29,30]\n[29,14,24,26,28,29,30,33,37,38]\n[30,26,28,29,30,37,38]\n[32]\n[33]\n[37]\n[38]\nRank 3\n[14]\n[15]\n[16]\n[17]\n[18,18,20,31,32]\n[19]\n[20,18,20,24,31,32,33]\n[21]\n[22]\n[23]\n[24,20,24,29,32,33,37]\n[26]\n[28]\n[29,24,29,30,33,37,38]\n[30,29,30,37,38]\n[31,18,20,31,32,34,35]\n[32,18,20,24,31,32,33,34,35,36]\n[33,20,24,29,32,33,35,36,37,39]\n[34,31,32,34,35]\n[35,31,32,33,34,35,36]\n[36,32,33,35,36,37,39]\n[37,24,29,30,33,36,37,38,39,40]\n[38,29,30,37,38,39,40]\n[39,33,36,37,38,39,40]\n[40,37,38,39,40]\n\nIndex sets (owned and extended relevant):\nRank 0\n{[0,14]}\n{[0,17], [20,22], [24,26], 29}\nRank 1\n{[15,24]}\n{[2,3], [5,8], [10,26], 29, [31,33], 37}\nRank 2\n{[25,30]}\n{[9,14], [20,30], [32,33], [37,38]}\nRank 3\n{[31,40]}\n{[14,24], 26, [28,40]}\n\nModified sparsity pattern (global numbering):\nRank 0\n[0,0,1,2,3]\n[1,1,0,2,3,4,5]\n[2,2,0,1,3,6,7]\n[3,3,0,1,2,4,5,6,7,8]\n[4,4,1,3,5,9,10]\n[5,5,1,3,4,7,8,9,10,13]\n[6,6,2,3,7,15,16]\n[7,7,2,3,5,6,8,15,16,17]\n[8,8,3,5,7,10,13,16,17,21]\n[9,9,4,5,10,11,14]\n[10,10,4,5,8,9,11,13,14]\n[11,11,9,10,13,14,25,26]\n[12,12]\n[13,13,5,8,10,11,14,17,21,24]\n[14,14,9,10,11,13,20,21,24,25,26,29]\n[15,15]\n[16,16]\n[17,17]\n[20,20]\n[21,21]\n[22,22]\n[24,24]\n[25,25]\n[26,26]\n[29,29]\nRank 1\n[15,15,16,18,20,6,7]\n[16,16,15,17,18,20,6,7,8]\n[17,17,16,18,20,21,24,7,8,13]\n[18,18,15,16,17,20,31,32]\n[19,19]\n[20,20,15,16,17,18,21,24,14,31,32,33]\n[21,21,17,20,24,8,13,14]\n[22,22]\n[23,23]\n[24,24,17,20,21,13,14,26,29,32,33,37]\n[2,2]\n[3,3]\n[5,5]\n[6,6,15,16,7]\n[7,7,15,16,17,6,8]\n[8,8,16,17,21,7,13]\n[10,10]\n[11,11]\n[12,12]\n[13,13,17,21,24,8,14]\n[14,14,20,21,24,13]\n[25,25]\n[26,26]\n[29,29]\n[31,31]\n[32,32]\n[33,33]\n[37,37]\nRank 2\n[25,25,26,27,28,11,14]\n[26,26,25,27,28,29,30,11,14,24]\n[27,27,25,26,28]\n[28,28,25,26,27,29,30]\n[29,29,26,28,30,14,24,33,37,38]\n[30,30,26,28,29,37,38]\n[9,9]\n[10,10]\n[11,11,25,26,14]\n[12,12]\n[13,13]\n[14,14,25,26,29,11,24]\n[20,20]\n[21,21]\n[22,22]\n[23,23]\n[24,24,26,29,14]\n[32,32]\n[33,33]\n[37,37]\n[38,38]\nRank 3\n[31,31,32,34,35,18,20]\n[32,32,31,33,34,35,36,18,20,24]\n[33,33,32,35,36,37,39,20,24,29]\n[34,34,31,32,35]\n[35,35,31,32,33,34,36]\n[36,36,32,33,35,37,39]\n[37,37,33,36,38,39,40,24,29,30]\n[38,38,37,39,40,29,30]\n[39,39,33,36,37,38,40]\n[40,40,37,38,39]\n[14,14]\n[15,15]\n[16,16]\n[17,17]\n[18,18,31,32,20]\n[19,19]\n[20,20,31,32,33,18,24]\n[21,21]\n[22,22]\n[23,23]\n[24,24,32,33,37,20,29]\n[26,26]\n[28,28]\n[29,29,33,37,38,24,30]\n[30,30,37,38,29]\n\nExchange pattern:\nRank 0\nReceive targets:\n1 : 7\n2 : 10\nSend targets:\n1 : 33\n2 : 47\n3 : 48\nEntries to be sent:\n2 : 0\n3 : 0\n5 : 0\n6 : 0\n6 : 4\n6 : 5\n6 : 3\n7 : 0\n7 : 6\n7 : 7\n7 : 8\n7 : 4\n7 : 5\n8 : 0\n8 : 6\n8 : 7\n8 : 8\n8 : 3\n8 : 5\n10 : 0\n11 : 0\n12 : 0\n13 : 0\n13 : 6\n13 : 7\n13 : 8\n13 : 2\n13 : 5\n14 : 0\n14 : 5\n14 : 6\n14 : 7\n14 : 4\n9 : 0\n10 : 0\n11 : 0\n11 : 5\n11 : 6\n11 : 4\n12 : 0\n13 : 0\n14 : 0\n14 : 8\n14 : 9\n14 : 10\n14 : 3\n14 : 7\n14 : 0\n\nRank 1\nReceive targets:\n0 : 33\n2 : 36\n3 : 40\nSend targets:\n0 : 7\n2 : 15\n3 : 38\nEntries to be sent:\n0 : 0\n1 : 0\n2 : 0\n5 : 0\n6 : 0\n7 : 0\n9 : 0\n5 : 0\n6 : 0\n7 : 0\n8 : 0\n9 : 0\n9 : 6\n9 : 7\n9 : 5\n0 : 0\n1 : 0\n2 : 0\n3 : 0\n3 : 5\n3 : 6\n3 : 4\n4 : 0\n5 : 0\n5 : 8\n5 : 9\n5 : 10\n5 : 4\n5 : 6\n6 : 0\n7 : 0\n8 : 0\n9 : 0\n9 : 8\n9 : 9\n9 : 10\n9 : 2\n9 : 7\n\nRank 2\nReceive targets:\n0 : 14\n1 : 22\n3 : 26\nSend targets:\n0 : 3\n1 : 6\n3 : 18\nEntries to be sent:\n0 : 0\n1 : 0\n4 : 0\n0 : 0\n1 : 0\n4 : 0\n1 : 0\n3 : 0\n4 : 0\n4 : 6\n4 : 7\n4 : 8\n4 : 5\n4 : 3\n5 : 0\n5 : 4\n5 : 5\n5 : 3\n\nRank 3\nReceive targets:\n0 : 1\n1 : 24\n2 : 36\nSend targets:\n1 : 4\n2 : 8\nEntries to be sent:\n0 : 0\n1 : 0\n2 : 0\n6 : 0\n1 : 0\n2 : 0\n6 : 0\n7 : 0\n\n\n\nSparse matrix contents:\nRank 0\n(0,0) 1\n(0,1) 1\n(0,2) 1\n(0,3) 1\n(1,1) 1\n(1,0) 1\n(1,2) 1\n(1,3) 1\n(1,4) 1\n(1,5) 1\n(2,2) 11\n(2,0) 1\n(2,1) 1\n(2,3) 1\n(2,6) 1\n(2,7) 1\n(3,3) 11\n(3,0) 1\n(3,1) 1\n(3,2) 1\n(3,4) 1\n(3,5) 1\n(3,6) 1\n(3,7) 1\n(3,8) 1\n(4,4) 1\n(4,1) 1\n(4,3) 1\n(4,5) 1\n(4,9) 1\n(4,10) 1\n(5,5) 11\n(5,1) 1\n(5,3) 1\n(5,4) 1\n(5,7) 1\n(5,8) 1\n(5,9) 1\n(5,10) 1\n(5,13) 1\n(6,6) 11\n(6,2) 1\n(6,3) 1\n(6,7) 11\n(6,15) 11\n(6,16) 11\n(7,7) 11\n(7,2) 1\n(7,3) 1\n(7,5) 1\n(7,6) 11\n(7,8) 11\n(7,15) 11\n(7,16) 11\n(7,17) 11\n(8,8) 11\n(8,3) 1\n(8,5) 1\n(8,7) 11\n(8,10) 1\n(8,13) 11\n(8,16) 11\n(8,17) 11\n(8,21) 11\n(9,9) 101\n(9,4) 1\n(9,5) 1\n(9,10) 1\n(9,11) 1\n(9,14) 1\n(10,10) 111\n(10,4) 1\n(10,5) 1\n(10,8) 1\n(10,9) 1\n(10,11) 1\n(10,13) 1\n(10,14) 1\n(11,11) 111\n(11,9) 1\n(11,10) 1\n(11,13) 1\n(11,14) 101\n(11,25) 101\n(11,26) 101\n(12,12) 111\n(13,13) 111\n(13,5) 1\n(13,8) 11\n(13,10) 1\n(13,11) 1\n(13,14) 11\n(13,17) 11\n(13,21) 11\n(13,24) 11\n(14,14) 1111\n(14,9) 1\n(14,10) 1\n(14,11) 101\n(14,13) 11\n(14,20) 11\n(14,21) 11\n(14,24) 111\n(14,25) 101\n(14,26) 101\n(14,29) 101\n(15,15) 0\n(16,16) 0\n(17,17) 0\n(20,20) 0\n(21,21) 0\n(22,22) 0\n(24,24) 0\n(25,25) 0\n(26,26) 0\n(29,29) 0\nRank 1\n(15,15) 1011\n(15,16) 10\n(15,18) 10\n(15,20) 10\n(15,6) 10\n(15,7) 10\n(16,16) 1011\n(16,15) 10\n(16,17) 10\n(16,18) 10\n(16,20) 10\n(16,6) 10\n(16,7) 10\n(16,8) 10\n(17,17) 1011\n(17,16) 10\n(17,18) 10\n(17,20) 10\n(17,21) 10\n(17,24) 10\n(17,7) 10\n(17,8) 10\n(17,13) 10\n(18,18) 1010\n(18,15) 10\n(18,16) 10\n(18,17) 10\n(18,20) 1010\n(18,31) 1010\n(18,32) 1010\n(19,19) 1010\n(20,20) 1111\n(20,15) 10\n(20,16) 10\n(20,17) 10\n(20,18) 1010\n(20,21) 10\n(20,24) 1010\n(20,14) 10\n(20,31) 1010\n(20,32) 1010\n(20,33) 1010\n(21,21) 1111\n(21,17) 10\n(21,20) 10\n(21,24) 10\n(21,8) 10\n(21,13) 10\n(21,14) 10\n(22,22) 1111\n(23,23) 1110\n(24,24) 1111\n(24,17) 10\n(24,20) 1010\n(24,21) 10\n(24,13) 10\n(24,14) 110\n(24,26) 110\n(24,29) 1110\n(24,32) 1010\n(24,33) 1010\n(24,37) 1010\n(2,2) 0\n(3,3) 0\n(5,5) 0\n(6,6) 0\n(6,15) 0\n(6,16) 0\n(6,7) 0\n(7,7) 0\n(7,15) 0\n(7,16) 0\n(7,17) 0\n(7,6) 0\n(7,8) 0\n(8,8) 0\n(8,16) 0\n(8,17) 0\n(8,21) 0\n(8,7) 0\n(8,13) 0\n(10,10) 0\n(11,11) 0\n(12,12) 0\n(13,13) 0\n(13,17) 0\n(13,21) 0\n(13,24) 0\n(13,8) 0\n(13,14) 0\n(14,14) 0\n(14,20) 0\n(14,21) 0\n(14,24) 0\n(14,13) 0\n(25,25) 0\n(26,26) 0\n(29,29) 0\n(31,31) 0\n(32,32) 0\n(33,33) 0\n(37,37) 0\nRank 2\n(25,25) 111\n(25,26) 100\n(25,27) 100\n(25,28) 100\n(25,11) 100\n(25,14) 100\n(26,26) 1111\n(26,25) 100\n(26,27) 100\n(26,28) 100\n(26,29) 100\n(26,30) 100\n(26,11) 100\n(26,14) 100\n(26,24) 100\n(27,27) 100\n(27,25) 100\n(27,26) 100\n(27,28) 100\n(28,28) 1100\n(28,25) 100\n(28,26) 100\n(28,27) 100\n(28,29) 100\n(28,30) 100\n(29,29) 1111\n(29,26) 100\n(29,28) 100\n(29,30) 1100\n(29,14) 100\n(29,24) 1100\n(29,33) 1100\n(29,37) 1100\n(29,38) 1100\n(30,30) 1100\n(30,26) 100\n(30,28) 100\n(30,29) 1100\n(30,37) 1100\n(30,38) 1100\n(9,9) 0\n(10,10) 0\n(11,11) 0\n(11,25) 0\n(11,26) 0\n(11,14) 0\n(12,12) 0\n(13,13) 0\n(14,14) 0\n(14,25) 0\n(14,26) 0\n(14,29) 0\n(14,11) 0\n(14,24) 0\n(20,20) 0\n(21,21) 0\n(22,22) 0\n(23,23) 0\n(24,24) 0\n(24,26) 0\n(24,29) 0\n(24,14) 0\n(32,32) 0\n(33,33) 0\n(37,37) 0\n(38,38) 0\nRank 3\n(31,31) 1010\n(31,32) 1000\n(31,34) 1000\n(31,35) 1000\n(31,18) 1000\n(31,20) 1000\n(32,32) 1110\n(32,31) 1000\n(32,33) 1000\n(32,34) 1000\n(32,35) 1000\n(32,36) 1000\n(32,18) 1000\n(32,20) 1000\n(32,24) 1000\n(33,33) 1110\n(33,32) 1000\n(33,35) 1000\n(33,36) 1000\n(33,37) 1000\n(33,39) 1000\n(33,20) 1000\n(33,24) 1000\n(33,29) 1000\n(34,34) 1000\n(34,31) 1000\n(34,32) 1000\n(34,35) 1000\n(35,35) 1000\n(35,31) 1000\n(35,32) 1000\n(35,33) 1000\n(35,34) 1000\n(35,36) 1000\n(36,36) 1000\n(36,32) 1000\n(36,33) 1000\n(36,35) 1000\n(36,37) 1000\n(36,39) 1000\n(37,37) 1110\n(37,33) 1000\n(37,36) 1000\n(37,38) 1000\n(37,39) 1000\n(37,40) 1000\n(37,24) 1000\n(37,29) 1000\n(37,30) 1000\n(38,38) 1100\n(38,37) 1000\n(38,39) 1000\n(38,40) 1000\n(38,29) 1000\n(38,30) 1000\n(39,39) 1000\n(39,33) 1000\n(39,36) 1000\n(39,37) 1000\n(39,38) 1000\n(39,40) 1000\n(40,40) 1000\n(40,37) 1000\n(40,38) 1000\n(40,39) 1000\n(14,14) 0\n(15,15) 0\n(16,16) 0\n(17,17) 0\n(18,18) 0\n(18,31) 0\n(18,32) 0\n(18,20) 0\n(19,19) 0\n(20,20) 0\n(20,31) 0\n(20,32) 0\n(20,33) 0\n(20,18) 0\n(20,24) 0\n(21,21) 0\n(22,22) 0\n(23,23) 0\n(24,24) 0\n(24,32) 0\n(24,33) 0\n(24,37) 0\n(24,20) 0\n(24,29) 0\n(26,26) 0\n(28,28) 0\n(29,29) 0\n(29,33) 0\n(29,37) 0\n(29,38) 0\n(29,24) 0\n(29,30) 0\n(30,30) 0\n(30,37) 0\n(30,38) 0\n(30,29) 0\n"
  },
  {
    "path": "tests/common/sparse_matrix_05.cc",
    "content": "#include <sparse_matrix.h>\n#include <sparsity_pattern.h>\n\n#include <deal.II/distributed/tria.h>\n#include <deal.II/dofs/dof_handler.h>\n#include <deal.II/dofs/dof_tools.h>\n#include <deal.II/fe/fe_q.h>\n#include <deal.II/grid/grid_generator.h>\n#include <deal.II/grid/grid_out.h>\n#include <deal.II/lac/sparsity_tools.h>\n\n#include <deal.II/lac/trilinos_sparse_matrix.h>\n\n#include <chrono>\n\n/*\n * Test distribute_local_to_global()\n */\n\nint main(int argc, char *argv[])\n{\n  using namespace std::chrono_literals;\n\n  dealii::Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv);\n\n  const auto mpi_rank =\n      dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD);\n\n  const auto n_mpi_processes =\n      dealii::Utilities::MPI::n_mpi_processes(MPI_COMM_WORLD);\n\n  /*\n   * Create a unit square twice globally refined and the bottom left\n   * quadrant once more. Creating a mesh with 4 hanging nodes.\n   */\n\n  constexpr int dim = 2;\n\n  dealii::parallel::distributed::Triangulation<dim> triangulation(\n      MPI_COMM_WORLD);\n  dealii::GridGenerator::hyper_cube(triangulation);\n  triangulation.refine_global(1);\n  triangulation.begin_active()->set_refine_flag();\n  triangulation.execute_coarsening_and_refinement();\n  triangulation.refine_global(1);\n\n  dealii::FE_Q<dim> fe(1);\n\n  /* Distribute DoFs and set up locally owned and relevant ranges: */\n\n  dealii::DoFHandler<dim> dof_handler(triangulation);\n  dof_handler.distribute_dofs(fe);\n\n  const dealii::IndexSet &locally_owned = dof_handler.locally_owned_dofs();\n\n  auto locally_relevant =\n      dealii::DoFTools::extract_locally_relevant_dofs(dof_handler);\n\n  dealii::AffineConstraints<double> affine_constraints;\n  affine_constraints.reinit(locally_owned, locally_relevant);\n  dealii::DoFTools::make_hanging_node_constraints(dof_handler,\n                                                  affine_constraints);\n  affine_constraints.close();\n\n  /* We should be consistent... */\n  if (!affine_constraints.is_consistent_in_parallel(\n          dealii::Utilities::MPI::all_gather(MPI_COMM_WORLD, locally_owned),\n          locally_relevant,\n          MPI_COMM_WORLD,\n          true)) {\n    std::cout << \"Oh Nooo!\" << std::endl;\n    __builtin_trap();\n  }\n\n  dealii::DynamicSparsityPattern dsp;\n  dsp.reinit(dof_handler.n_dofs(), dof_handler.n_dofs(), locally_relevant);\n  dealii::DoFTools::make_sparsity_pattern(\n      dof_handler, dsp, affine_constraints, false);\n  dealii::SparsityTools::distribute_sparsity_pattern(\n      dsp, locally_owned, MPI_COMM_WORLD, locally_relevant);\n\n  /* Enlarge the locally relevant set to include all additional couplings: */\n\n  dealii::IndexSet additional_dofs(dof_handler.n_dofs());\n  for (auto &entry : dsp)\n    if (!locally_relevant.is_element(entry.column())) {\n      Assert(locally_owned.is_element(entry.row()), dealii::ExcInternalError());\n      additional_dofs.add_index(entry.column());\n    }\n  additional_dofs.compress();\n  locally_relevant.add_indices(additional_dofs);\n  locally_relevant.compress();\n  const auto n_locally_relevant = locally_relevant.n_elements();\n\n  const auto partitioner =\n      std::make_shared<dealii::Utilities::MPI::Partitioner>(\n          locally_owned, locally_relevant, MPI_COMM_WORLD);\n\n  /* Create final sparsity pattern: */\n\n  using VA = dealii::VectorizedArray<double>;\n  constexpr auto simd_width = VA::size();\n  ryujin::SparsityPattern<simd_width> sparsity_pattern(0, dsp, partitioner);\n\n  /* Create a sparse matrix: */\n\n  ryujin::SparseMatrix<double, 1, simd_width> sparse_matrix;\n  sparse_matrix.reinit(sparsity_pattern);\n\n  /* Add a local contribution from all owning cells: */\n  for (const auto &cell : dof_handler.active_cell_iterators()) {\n    if (!cell->is_locally_owned())\n      continue;\n\n    const unsigned int dofs_per_cell = cell->get_fe().n_dofs_per_cell();\n    std::vector<dealii::types::global_dof_index> dof_indices(dofs_per_cell);\n    cell->get_dof_indices(dof_indices);\n\n    dealii::FullMatrix<double> cell_matrix(dofs_per_cell, dofs_per_cell);\n\n    for (unsigned int i = 0; i < dofs_per_cell; ++i)\n      for (unsigned int j = 0; j < dofs_per_cell; ++j)\n        if (i == j)\n          cell_matrix(i, j) = std::pow(10., mpi_rank);\n        else\n          cell_matrix(i, j) = -std::pow(10., mpi_rank);\n\n    ryujin::distribute_local_to_global(\n        cell_matrix, dof_indices, affine_constraints, sparse_matrix);\n  }\n\n  sparse_matrix.compress(dealii::VectorOperation::add);\n\n  const auto print_matrix = [&]() {\n    for (unsigned int i = 0; i < n_locally_relevant; ++i) {\n      const auto i_global = partitioner->local_to_global(i);\n      const unsigned int row_length = sparsity_pattern.row_length(i);\n      const unsigned int *js = sparsity_pattern.columns(i);\n      for (unsigned int col_idx = 0; col_idx < row_length; ++col_idx, ++js) {\n        const auto j_global = partitioner->local_to_global(*js);\n        std::cout << \"(\" << i_global << \",\" << j_global << \") \"\n                  << sparse_matrix.read_entry(i, col_idx) << std::endl;\n      }\n    }\n  };\n\n  for (unsigned int i = 0; i < n_mpi_processes; ++i) {\n    if (i == mpi_rank) {\n      if (mpi_rank == 0)\n        std::cout << \"\\n\\nSparse matrix contents:\\n\";\n      std::cout << \"Rank \" << mpi_rank << std::endl;\n      print_matrix();\n    }\n    std::this_thread::sleep_for(200ms);\n    MPI_Barrier(MPI_COMM_WORLD);\n  }\n}\n"
  },
  {
    "path": "tests/common/sparse_matrix_05.mpirun=4.threads=2.output",
    "content": "\n\nSparse matrix contents:\nRank 0\n(0,0) 1\n(0,1) -1\n(0,2) -1\n(0,3) -1\n(1,1) 2\n(1,0) -1\n(1,2) -1\n(1,3) -2\n(1,4) -1\n(1,5) -1\n(2,2) 2\n(2,0) -1\n(2,1) -1\n(2,3) -2\n(2,6) -1\n(2,7) -1\n(3,3) 4\n(3,0) -1\n(3,1) -2\n(3,2) -2\n(3,4) -1\n(3,5) -2\n(3,6) -1\n(3,7) -2\n(3,8) -1\n(4,4) 2\n(4,1) -1\n(4,3) -1\n(4,5) -2\n(4,9) -1\n(4,10) -1\n(5,5) 4\n(5,1) -1\n(5,3) -2\n(5,4) -2\n(5,7) -1\n(5,8) -2\n(5,9) -1\n(5,10) -2\n(5,13) -1\n(6,6) 11\n(6,2) -1\n(6,3) -1\n(6,7) -11\n(6,15) -10\n(6,16) -10\n(7,7) 22\n(7,2) -1\n(7,3) -2\n(7,5) -1\n(7,6) -11\n(7,8) -11\n(7,15) -10\n(7,16) -20\n(7,17) -10\n(8,8) 22\n(8,3) -1\n(8,5) -2\n(8,7) -11\n(8,10) -1\n(8,13) -11\n(8,16) -10\n(8,17) -20\n(8,21) -10\n(9,9) 2\n(9,4) -1\n(9,5) -1\n(9,10) -2\n(9,11) -1.5\n(9,14) -0.5\n(10,10) 4\n(10,4) -1\n(10,5) -2\n(10,8) -1\n(10,9) -2\n(10,11) -2\n(10,13) -2\n(10,14) -2\n(11,11) 100.5\n(11,9) -1.5\n(11,10) -2\n(11,13) -0.5\n(11,14) -100.5\n(11,25) -100\n(11,26) -100\n(12,12) 0\n(13,13) 22\n(13,5) -1\n(13,8) -11\n(13,10) -2\n(13,11) -0.5\n(13,14) -16.5\n(13,17) -10\n(13,21) -20\n(13,24) -5\n(14,14) 205.5\n(14,9) -0.5\n(14,10) -2\n(14,11) -100.5\n(14,13) -16.5\n(14,20) -2.5\n(14,21) -20\n(14,24) -107.5\n(14,25) -100\n(14,26) -200\n(14,29) -100\n(15,15) 0\n(15,6) 0\n(15,7) 0\n(16,16) 0\n(16,6) 0\n(16,7) 0\n(16,8) 0\n(17,17) 0\n(17,7) 0\n(17,8) 0\n(17,13) 0\n(20,20) 0\n(20,14) 0\n(21,21) 0\n(21,8) 0\n(21,13) 0\n(21,14) 0\n(22,22) 0\n(24,24) 0\n(24,13) 0\n(24,14) 0\n(25,25) 0\n(25,11) 0\n(25,14) 0\n(26,26) 0\n(26,11) 0\n(26,14) 0\n(29,29) 0\n(29,14) 0\nRank 1\n(15,15) 20\n(15,16) -20\n(15,18) -15\n(15,20) -5\n(15,6) -10\n(15,7) -10\n(16,16) 40\n(16,15) -20\n(16,17) -20\n(16,18) -20\n(16,20) -20\n(16,6) -10\n(16,7) -20\n(16,8) -10\n(17,17) 40\n(17,16) -20\n(17,18) -5\n(17,20) -30\n(17,21) -20\n(17,24) -5\n(17,7) -10\n(17,8) -20\n(17,13) -10\n(18,18) 1005\n(18,15) -15\n(18,16) -20\n(18,17) -5\n(18,20) -1005\n(18,31) -1000\n(18,32) -1000\n(19,19) 0\n(20,20) 2010\n(20,15) -5\n(20,16) -20\n(20,17) -30\n(20,18) -1005\n(20,21) -20\n(20,24) -1007.5\n(20,14) -2.5\n(20,31) -1000\n(20,32) -2000\n(20,33) -1000\n(21,21) 40\n(21,17) -20\n(21,20) -20\n(21,24) -30\n(21,8) -10\n(21,13) -20\n(21,14) -20\n(22,22) 0\n(23,23) 0\n(24,24) 2095\n(24,17) -5\n(24,20) -1007.5\n(24,21) -30\n(24,13) -5\n(24,14) -107.5\n(24,26) -100\n(24,29) -1100\n(24,32) -1000\n(24,33) -2000\n(24,37) -1000\n(2,2) 0\n(3,3) 0\n(5,5) 0\n(6,6) 0\n(6,15) 0\n(6,16) 0\n(6,7) 0\n(7,7) 0\n(7,15) 0\n(7,16) 0\n(7,17) 0\n(7,6) 0\n(7,8) 0\n(8,8) 0\n(8,16) 0\n(8,17) 0\n(8,21) 0\n(8,7) 0\n(8,13) 0\n(10,10) 0\n(11,11) 0\n(12,12) 0\n(13,13) 0\n(13,17) 0\n(13,21) 0\n(13,24) 0\n(13,8) 0\n(13,14) 0\n(14,14) 0\n(14,20) 0\n(14,21) 0\n(14,24) 0\n(14,13) 0\n(25,25) 0\n(26,26) 0\n(26,24) 0\n(29,29) 0\n(29,24) 0\n(31,31) 0\n(31,18) 0\n(31,20) 0\n(32,32) 0\n(32,18) 0\n(32,20) 0\n(32,24) 0\n(33,33) 0\n(33,20) 0\n(33,24) 0\n(37,37) 0\n(37,24) 0\nRank 2\n(25,25) 200\n(25,26) -200\n(25,27) -100\n(25,28) -100\n(25,11) -100\n(25,14) -100\n(26,26) 400\n(26,25) -200\n(26,27) -100\n(26,28) -200\n(26,29) -200\n(26,30) -100\n(26,11) -100\n(26,14) -200\n(26,24) -100\n(27,27) 100\n(27,25) -100\n(27,26) -100\n(27,28) -100\n(28,28) 200\n(28,25) -100\n(28,26) -200\n(28,27) -100\n(28,29) -100\n(28,30) -100\n(29,29) 2200\n(29,26) -200\n(29,28) -100\n(29,30) -1100\n(29,14) -100\n(29,24) -1100\n(29,33) -1000\n(29,37) -2000\n(29,38) -1000\n(30,30) 1100\n(30,26) -100\n(30,28) -100\n(30,29) -1100\n(30,37) -1000\n(30,38) -1000\n(9,9) 0\n(10,10) 0\n(11,11) 0\n(11,25) 0\n(11,26) 0\n(11,14) 0\n(12,12) 0\n(13,13) 0\n(14,14) 0\n(14,25) 0\n(14,26) 0\n(14,29) 0\n(14,11) 0\n(14,24) 0\n(20,20) 0\n(21,21) 0\n(22,22) 0\n(23,23) 0\n(24,24) 0\n(24,26) 0\n(24,29) 0\n(24,14) 0\n(32,32) 0\n(33,33) 0\n(33,29) 0\n(37,37) 0\n(37,29) 0\n(37,30) 0\n(38,38) 0\n(38,29) 0\n(38,30) 0\nRank 3\n(31,31) 2000\n(31,32) -2000\n(31,34) -1000\n(31,35) -1000\n(31,18) -1000\n(31,20) -1000\n(32,32) 4000\n(32,31) -2000\n(32,33) -2000\n(32,34) -1000\n(32,35) -2000\n(32,36) -1000\n(32,18) -1000\n(32,20) -2000\n(32,24) -1000\n(33,33) 4000\n(33,32) -2000\n(33,35) -1000\n(33,36) -2000\n(33,37) -2000\n(33,39) -1000\n(33,20) -1000\n(33,24) -2000\n(33,29) -1000\n(34,34) 1000\n(34,31) -1000\n(34,32) -1000\n(34,35) -1000\n(35,35) 2000\n(35,31) -1000\n(35,32) -2000\n(35,33) -1000\n(35,34) -1000\n(35,36) -1000\n(36,36) 2000\n(36,32) -1000\n(36,33) -2000\n(36,35) -1000\n(36,37) -1000\n(36,39) -1000\n(37,37) 4000\n(37,33) -2000\n(37,36) -1000\n(37,38) -2000\n(37,39) -2000\n(37,40) -1000\n(37,24) -1000\n(37,29) -2000\n(37,30) -1000\n(38,38) 2000\n(38,37) -2000\n(38,39) -1000\n(38,40) -1000\n(38,29) -1000\n(38,30) -1000\n(39,39) 2000\n(39,33) -1000\n(39,36) -1000\n(39,37) -2000\n(39,38) -1000\n(39,40) -1000\n(40,40) 1000\n(40,37) -1000\n(40,38) -1000\n(40,39) -1000\n(14,14) 0\n(15,15) 0\n(16,16) 0\n(17,17) 0\n(18,18) 0\n(18,31) 0\n(18,32) 0\n(18,20) 0\n(19,19) 0\n(20,20) 0\n(20,31) 0\n(20,32) 0\n(20,33) 0\n(20,18) 0\n(20,24) 0\n(21,21) 0\n(22,22) 0\n(23,23) 0\n(24,24) 0\n(24,32) 0\n(24,33) 0\n(24,37) 0\n(24,20) 0\n(24,29) 0\n(26,26) 0\n(28,28) 0\n(29,29) 0\n(29,33) 0\n(29,37) 0\n(29,38) 0\n(29,24) 0\n(29,30) 0\n(30,30) 0\n(30,37) 0\n(30,38) 0\n(30,29) 0\n"
  },
  {
    "path": "tests/common/sparsity_pattern_01.cc",
    "content": "#include <sparse_matrix.h>\n#include <sparsity_pattern.h>\n\n#include <deal.II/distributed/tria.h>\n#include <deal.II/dofs/dof_handler.h>\n#include <deal.II/dofs/dof_tools.h>\n#include <deal.II/fe/fe_q.h>\n#include <deal.II/grid/grid_generator.h>\n#include <deal.II/grid/grid_out.h>\n#include <deal.II/lac/sparsity_tools.h>\n\n#include <deal.II/lac/trilinos_sparse_matrix.h>\n\n#include <chrono>\n\n/*\n * Test SparseMatrix::compress(VectorOperation::add)\n */\n\nnamespace ryujin\n{\n  template <int simd_length>\n  class Debug : public SparsityPattern<simd_length>\n  {\n  public:\n    Debug(const unsigned int n_internal_dofs,\n          const dealii::DynamicSparsityPattern &sparsity,\n          const std::shared_ptr<const dealii::Utilities::MPI::Partitioner>\n              &partitioner)\n        : SparsityPattern<simd_length>(n_internal_dofs,\n                                       sparsity,\n                                       partitioner,\n                                       /*symmetrize ghost range*/ true)\n    {\n    }\n\n    void print()\n    {\n      std::stringstream ss;\n\n      ss << \"Receive targets:\\n\";\n      for (const auto &[left, right] : this->receive_targets())\n        ss << left << \" : \" << right << \"\\n\";\n\n      ss << \"Send targets:\\n\";\n      for (const auto &[left, right] : this->send_targets())\n        ss << left << \" : \" << right << \"\\n\";\n\n      ss << \"Entries to be sent:\\n\";\n      for (const auto &[left, right] : this->entries_to_be_sent())\n        ss << left << \" : \" << right << \"\\n\";\n\n      std::cout << ss.str() << std::endl;\n    }\n  };\n} // namespace ryujin\n\n\nint main(int argc, char *argv[])\n{\n  using namespace std::chrono_literals;\n\n  dealii::Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv);\n\n  const auto mpi_rank =\n      dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD);\n\n  const auto n_mpi_processes =\n      dealii::Utilities::MPI::n_mpi_processes(MPI_COMM_WORLD);\n\n  /*\n   * Create a unit square twice globally refined and the bottom left\n   * quadrant once more. Creating a mesh with 4 hanging nodes.\n   */\n\n  constexpr int dim = 2;\n\n  dealii::parallel::distributed::Triangulation<dim> triangulation(\n      MPI_COMM_WORLD);\n  dealii::GridGenerator::hyper_cube(triangulation);\n  triangulation.refine_global(1);\n  triangulation.begin_active()->set_refine_flag();\n  triangulation.execute_coarsening_and_refinement();\n  triangulation.refine_global(1);\n\n  dealii::FE_Q<dim> fe(1);\n\n  /*\n   * Distribute DoFs, set up locally owned and relevant ranges, and partitioner.\n   */\n\n  dealii::DoFHandler<dim> dof_handler(triangulation);\n  dof_handler.distribute_dofs(fe);\n\n  const dealii::IndexSet &locally_owned = dof_handler.locally_owned_dofs();\n\n  auto locally_relevant =\n      dealii::DoFTools::extract_locally_relevant_dofs(dof_handler);\n\n  dealii::AffineConstraints<double> affine_constraints;\n  affine_constraints.reinit(locally_owned, locally_relevant);\n  dealii::DoFTools::make_hanging_node_constraints(dof_handler,\n                                                  affine_constraints);\n  affine_constraints.close();\n\n  /* We should be consistent... */\n  if (!affine_constraints.is_consistent_in_parallel(\n          dealii::Utilities::MPI::all_gather(MPI_COMM_WORLD, locally_owned),\n          locally_relevant,\n          MPI_COMM_WORLD,\n          true)) {\n    std::cout << \"Oh Nooo!\" << std::endl;\n    __builtin_trap();\n  }\n\n  dealii::DynamicSparsityPattern dsp;\n  dsp.reinit(dof_handler.n_dofs(), dof_handler.n_dofs(), locally_relevant);\n\n  /* Create temporary sparsity pattern: */\n  dealii::DoFTools::make_sparsity_pattern(\n      dof_handler, dsp, affine_constraints, false);\n  dealii::SparsityTools::distribute_sparsity_pattern(\n      dsp, locally_owned, MPI_COMM_WORLD, locally_relevant);\n\n  for (unsigned int i = 0; i < n_mpi_processes; ++i) {\n    if (i == mpi_rank) {\n      if (mpi_rank == 0)\n        std::cout << \"\\nPreliminary sparsity pattern (global numbering):\\n\";\n      std::cout << \"Rank \" << mpi_rank << std::endl;\n      dsp.print(std::cout);\n    }\n    std::this_thread::sleep_for(200ms);\n    MPI_Barrier(MPI_COMM_WORLD);\n  }\n\n  /*\n   * Enlarge the locally relevant set to include all additional couplings:\n   */\n\n  dealii::IndexSet additional_dofs(dof_handler.n_dofs());\n  for (auto &entry : dsp)\n    if (!locally_relevant.is_element(entry.column())) {\n      Assert(locally_owned.is_element(entry.row()), dealii::ExcInternalError());\n      additional_dofs.add_index(entry.column());\n    }\n  additional_dofs.compress();\n  locally_relevant.add_indices(additional_dofs);\n  locally_relevant.compress();\n  const auto n_locally_relevant = locally_relevant.n_elements();\n\n  for (unsigned int i = 0; i < n_mpi_processes; ++i) {\n    if (i == mpi_rank) {\n      if (mpi_rank == 0)\n        std::cout << \"\\nIndex sets (owned and extended relevant):\\n\";\n      std::cout << \"Rank \" << mpi_rank << std::endl;\n      locally_owned.print(std::cout);\n      locally_relevant.print(std::cout);\n    }\n    std::this_thread::sleep_for(200ms);\n    MPI_Barrier(MPI_COMM_WORLD);\n  }\n\n  const auto partitioner =\n      std::make_shared<dealii::Utilities::MPI::Partitioner>(\n          locally_owned, locally_relevant, MPI_COMM_WORLD);\n\n  /*\n   * Create final sparsity pattern:\n   */\n\n  using VA = dealii::VectorizedArray<double>;\n  constexpr auto simd_width = VA::size();\n  ryujin::Debug<simd_width> sparsity_pattern(0, dsp, partitioner);\n  const auto print_sparsity = [&]() {\n    for (unsigned int i = 0; i < n_locally_relevant; ++i) {\n      const auto i_global = partitioner->local_to_global(i);\n      const unsigned int row_length = sparsity_pattern.row_length(i);\n      const unsigned int *js = sparsity_pattern.columns(i);\n      std::cout << \"[\" << i_global;\n      for (unsigned int col_idx = 0; col_idx < row_length; ++col_idx, ++js) {\n        const auto j_global = partitioner->local_to_global(*js);\n        std::cout << \",\" << j_global;\n      }\n      std::cout << \"]\" << std::endl;\n    }\n  };\n\n  for (unsigned int i = 0; i < n_mpi_processes; ++i) {\n    if (i == mpi_rank) {\n      if (mpi_rank == 0)\n        std::cout << \"\\nModified sparsity pattern (global numbering):\\n\";\n      std::cout << \"Rank \" << mpi_rank << std::endl;\n      print_sparsity();\n    }\n    std::this_thread::sleep_for(200ms);\n    MPI_Barrier(MPI_COMM_WORLD);\n  }\n\n  for (unsigned int i = 0; i < n_mpi_processes; ++i) {\n    if (i == mpi_rank) {\n      if (mpi_rank == 0)\n        std::cout << \"\\nExchange pattern:\\n\";\n      std::cout << \"Rank \" << mpi_rank << std::endl;\n      sparsity_pattern.print();\n    }\n    std::this_thread::sleep_for(200ms);\n    MPI_Barrier(MPI_COMM_WORLD);\n  }\n}\n"
  },
  {
    "path": "tests/common/sparsity_pattern_01.mpirun=4.threads=2.output",
    "content": "\nPreliminary sparsity pattern (global numbering):\nRank 0\n[0,0,1,2,3]\n[1,0,1,2,3,4,5]\n[2,0,1,2,3,6,7]\n[3,0,1,2,3,4,5,6,7,8]\n[4,1,3,4,5,9,10]\n[5,1,3,4,5,7,8,9,10,13]\n[6,2,3,6,7,15,16]\n[7,2,3,5,6,7,8,15,16,17]\n[8,3,5,7,8,10,13,16,17,21]\n[9,4,5,9,10,11,14]\n[10,4,5,8,9,10,11,13,14]\n[11,9,10,11,13,14,25,26]\n[12,12]\n[13,5,8,10,11,13,14,17,21,24]\n[14,9,10,11,13,14,20,21,24,25,26,29]\n[15]\n[16]\n[17]\n[21]\n[22]\n[24]\n[25]\n[26]\n[29]\nRank 1\n[2]\n[3]\n[5]\n[6,6,7,15,16]\n[7,6,7,8,15,16,17]\n[8,7,8,13,16,17,21]\n[10]\n[11]\n[12]\n[13,8,13,14,17,21,24]\n[14,13,14,20,21,24]\n[15,6,7,15,16,18,20]\n[16,6,7,8,15,16,17,18,20]\n[17,7,8,13,16,17,18,20,21,24]\n[18,15,16,17,18,20,31,32]\n[19,19]\n[20,14,15,16,17,18,20,21,24,31,32,33]\n[21,8,13,14,17,20,21,24]\n[22,22]\n[23,23]\n[24,13,14,17,20,21,24,26,29,32,33,37]\n[25]\n[26]\n[29]\n[31]\n[32]\n[33]\n[37]\nRank 2\n[9]\n[10]\n[11,11,14,25,26]\n[12]\n[13]\n[14,11,14,24,25,26,29]\n[20]\n[21]\n[22]\n[23]\n[24,14,24,26,29]\n[25,11,14,25,26,27,28]\n[26,11,14,24,25,26,27,28,29,30]\n[27,25,26,27,28]\n[28,25,26,27,28,29,30]\n[29,14,24,26,28,29,30,33,37,38]\n[30,26,28,29,30,37,38]\n[32]\n[33]\n[37]\n[38]\nRank 3\n[14]\n[15]\n[16]\n[17]\n[18,18,20,31,32]\n[19]\n[20,18,20,24,31,32,33]\n[21]\n[22]\n[23]\n[24,20,24,29,32,33,37]\n[26]\n[28]\n[29,24,29,30,33,37,38]\n[30,29,30,37,38]\n[31,18,20,31,32,34,35]\n[32,18,20,24,31,32,33,34,35,36]\n[33,20,24,29,32,33,35,36,37,39]\n[34,31,32,34,35]\n[35,31,32,33,34,35,36]\n[36,32,33,35,36,37,39]\n[37,24,29,30,33,36,37,38,39,40]\n[38,29,30,37,38,39,40]\n[39,33,36,37,38,39,40]\n[40,37,38,39,40]\n\nIndex sets (owned and extended relevant):\nRank 0\n{[0,14]}\n{[0,17], [20,22], [24,26], 29}\nRank 1\n{[15,24]}\n{[2,3], [5,8], [10,26], 29, [31,33], 37}\nRank 2\n{[25,30]}\n{[9,14], [20,30], [32,33], [37,38]}\nRank 3\n{[31,40]}\n{[14,24], 26, [28,40]}\n\nModified sparsity pattern (global numbering):\nRank 0\n[0,0,1,2,3]\n[1,1,0,2,3,4,5]\n[2,2,0,1,3,6,7]\n[3,3,0,1,2,4,5,6,7,8]\n[4,4,1,3,5,9,10]\n[5,5,1,3,4,7,8,9,10,13]\n[6,6,2,3,7,15,16]\n[7,7,2,3,5,6,8,15,16,17]\n[8,8,3,5,7,10,13,16,17,21]\n[9,9,4,5,10,11,14]\n[10,10,4,5,8,9,11,13,14]\n[11,11,9,10,13,14,25,26]\n[12,12]\n[13,13,5,8,10,11,14,17,21,24]\n[14,14,9,10,11,13,20,21,24,25,26,29]\n[15,15,6,7]\n[16,16,6,7,8]\n[17,17,7,8,13]\n[20,20,14]\n[21,21,8,13,14]\n[22,22]\n[24,24,13,14]\n[25,25,11,14]\n[26,26,11,14]\n[29,29,14]\nRank 1\n[15,15,16,18,20,6,7]\n[16,16,15,17,18,20,6,7,8]\n[17,17,16,18,20,21,24,7,8,13]\n[18,18,15,16,17,20,31,32]\n[19,19]\n[20,20,15,16,17,18,21,24,14,31,32,33]\n[21,21,17,20,24,8,13,14]\n[22,22]\n[23,23]\n[24,24,17,20,21,13,14,26,29,32,33,37]\n[2,2]\n[3,3]\n[5,5]\n[6,6,15,16,7]\n[7,7,15,16,17,6,8]\n[8,8,16,17,21,7,13]\n[10,10]\n[11,11]\n[12,12]\n[13,13,17,21,24,8,14]\n[14,14,20,21,24,13]\n[25,25]\n[26,26,24]\n[29,29,24]\n[31,31,18,20]\n[32,32,18,20,24]\n[33,33,20,24]\n[37,37,24]\nRank 2\n[25,25,26,27,28,11,14]\n[26,26,25,27,28,29,30,11,14,24]\n[27,27,25,26,28]\n[28,28,25,26,27,29,30]\n[29,29,26,28,30,14,24,33,37,38]\n[30,30,26,28,29,37,38]\n[9,9]\n[10,10]\n[11,11,25,26,14]\n[12,12]\n[13,13]\n[14,14,25,26,29,11,24]\n[20,20]\n[21,21]\n[22,22]\n[23,23]\n[24,24,26,29,14]\n[32,32]\n[33,33,29]\n[37,37,29,30]\n[38,38,29,30]\nRank 3\n[31,31,32,34,35,18,20]\n[32,32,31,33,34,35,36,18,20,24]\n[33,33,32,35,36,37,39,20,24,29]\n[34,34,31,32,35]\n[35,35,31,32,33,34,36]\n[36,36,32,33,35,37,39]\n[37,37,33,36,38,39,40,24,29,30]\n[38,38,37,39,40,29,30]\n[39,39,33,36,37,38,40]\n[40,40,37,38,39]\n[14,14]\n[15,15]\n[16,16]\n[17,17]\n[18,18,31,32,20]\n[19,19]\n[20,20,31,32,33,18,24]\n[21,21]\n[22,22]\n[23,23]\n[24,24,32,33,37,20,29]\n[26,26]\n[28,28]\n[29,29,33,37,38,24,30]\n[30,30,37,38,29]\n\nExchange pattern:\nRank 0\nReceive targets:\n1 : 21\n2 : 29\nSend targets:\n1 : 33\n2 : 47\n3 : 48\nEntries to be sent:\n2 : 0\n3 : 0\n5 : 0\n6 : 0\n6 : 4\n6 : 5\n6 : 3\n7 : 0\n7 : 6\n7 : 7\n7 : 8\n7 : 4\n7 : 5\n8 : 0\n8 : 6\n8 : 7\n8 : 8\n8 : 3\n8 : 5\n10 : 0\n11 : 0\n12 : 0\n13 : 0\n13 : 6\n13 : 7\n13 : 8\n13 : 2\n13 : 5\n14 : 0\n14 : 5\n14 : 6\n14 : 7\n14 : 4\n9 : 0\n10 : 0\n11 : 0\n11 : 5\n11 : 6\n11 : 4\n12 : 0\n13 : 0\n14 : 0\n14 : 8\n14 : 9\n14 : 10\n14 : 3\n14 : 7\n14 : 0\n\nRank 1\nReceive targets:\n0 : 33\n2 : 38\n3 : 50\nSend targets:\n0 : 21\n2 : 29\n3 : 52\nEntries to be sent:\n0 : 0\n0 : 4\n0 : 5\n1 : 0\n1 : 5\n1 : 6\n1 : 7\n2 : 0\n2 : 6\n2 : 7\n2 : 8\n5 : 0\n5 : 7\n6 : 0\n6 : 4\n6 : 5\n6 : 6\n7 : 0\n9 : 0\n9 : 4\n9 : 5\n5 : 0\n6 : 0\n7 : 0\n8 : 0\n9 : 0\n9 : 6\n9 : 7\n9 : 5\n0 : 0\n1 : 0\n2 : 0\n3 : 0\n3 : 5\n3 : 6\n3 : 4\n4 : 0\n5 : 0\n5 : 8\n5 : 9\n5 : 10\n5 : 4\n5 : 6\n6 : 0\n7 : 0\n8 : 0\n9 : 0\n9 : 8\n9 : 9\n9 : 10\n9 : 2\n9 : 7\n\nRank 2\nReceive targets:\n0 : 14\n1 : 22\n3 : 31\nSend targets:\n0 : 8\n1 : 13\n3 : 25\nEntries to be sent:\n0 : 0\n0 : 4\n0 : 5\n1 : 0\n1 : 6\n1 : 7\n4 : 0\n4 : 4\n0 : 0\n1 : 0\n1 : 8\n4 : 0\n4 : 5\n1 : 0\n3 : 0\n4 : 0\n4 : 6\n4 : 7\n4 : 8\n4 : 5\n4 : 3\n5 : 0\n5 : 4\n5 : 5\n5 : 3\n\nRank 3\nReceive targets:\n0 : 1\n1 : 24\n2 : 36\nSend targets:\n1 : 12\n2 : 21\nEntries to be sent:\n0 : 0\n0 : 4\n0 : 5\n1 : 0\n1 : 6\n1 : 7\n1 : 8\n2 : 0\n2 : 6\n2 : 7\n6 : 0\n6 : 6\n1 : 0\n2 : 0\n2 : 8\n6 : 0\n6 : 7\n6 : 8\n7 : 0\n7 : 4\n7 : 5\n\n"
  },
  {
    "path": "tests/euler/CMakeLists.txt",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2022 - 2024 by the ryujin authors\n##\n\nset(EQUATION euler)\n\ninclude_directories(\n  ${CMAKE_BINARY_DIR}/source/\n  ${CMAKE_SOURCE_DIR}/source/${EQUATION}\n  ${CMAKE_SOURCE_DIR}/source/\n  )\n\nset(TEST_LIBRARIES obj_common obj_${EQUATION} obj_${EQUATION}_dependent)\nset(TEST_TARGET ryujin)\n\nif(TARGET obj_${EQUATION})\n  deal_ii_pickup_tests()\nendif()\n"
  },
  {
    "path": "tests/euler/amr-global_refinement.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = check_global_refinement\n\n  set enable compute error      = true\n  set enable mesh adaptivity    = true\n\n  set final time                = 2.0\n  set timer granularity         = 0.5\n\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n  set gamma     = 1.4\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 4\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.2\n  set cfl max            = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n\nsubsection I - MeshAdaptor\n  set adaptation strategy           = global refinement\n  set time point selection strategy = fixed time points\n  subsection time point selection strategies\n    set fixed time points = 1.0, 1.5\n  end\nend\n"
  },
  {
    "path": "tests/euler/amr-global_refinement.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\n[INFO] performing mesh adaptation\n[INFO] preparing compute kernels\n[INFO] performing mesh adaptation\n[INFO] preparing compute kernels\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 4225\nt     = 2.005477940653737\nLinf  = 0.3878953679336107\nL1    = 0.02909818323076671\nL2    = 0.0681479109883886\n"
  },
  {
    "path": "tests/euler/amr-mass_conservation_01.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = check_mass\n\n  set enable mesh adaptivity    = true\n  set enable compute quantities = true\n\n  set final time                = 2.0\n  set timer granularity         = 0.5\n\n  set terminal update interval  = 0\n\n  set debug filename                      = check_mass-interior-R0004-space_averaged_time_series.dat\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n  set gamma     = 1.4\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 4\n\n  subsection rectangular domain\n    set boundary condition bottom = slip\n    set boundary condition left   = slip\n    set boundary condition right  = slip\n    set boundary condition top    = slip\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.2\n  set cfl max            = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n\nsubsection I - MeshAdaptor\n  set adaptation strategy           = random adaptation\n  set time point selection strategy = fixed time points\n\n  subsection time point selection strategies\n    set fixed time points = 0.5, 1.0, 1.5\n  end\n\n  subsection marking strategies\n    set coarsening threshold = 0.80\n    set refinement threshold = 0.90\n  end\nend\n\nsubsection K - Quantities\n  set interior manifolds = interior : 0. : space_averaged\nend\n"
  },
  {
    "path": "tests/euler/amr-mass_conservation_01.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\n[INFO] performing mesh adaptation\n[INFO] preparing compute kernels\n[INFO] performing mesh adaptation\n[INFO] preparing compute kernels\n[INFO] performing mesh adaptation\n[INFO] preparing compute kernels\n# time t\tprimitive state (rho, v_1, v_2, p)\t and 2nd moments\n1.52477372883113e+00\t9.82417460447615e-01 4.52636588563042e-01 4.56309517512311e-01 1.06365397824689e+00\t1.20555200654202e+00 3.15944682683708e-01 3.16044216853591e-01 1.75093960321736e+00\n1.53633529266740e+00\t9.82417460447615e-01 4.50479179694512e-01 4.54059821343228e-01 1.06426205350032e+00\t1.20765405869174e+00 3.14562079914380e-01 3.14527726116660e-01 1.75795995968927e+00\n1.54795038236458e+00\t9.82417460447614e-01 4.48314656308866e-01 4.51799755840560e-01 1.06487220730736e+00\t1.20976756236573e+00 3.13176900247001e-01 3.13007327850067e-01 1.76502346403528e+00\n1.55961906608829e+00\t9.82417460447615e-01 4.46143599363536e-01 4.49529354875578e-01 1.06548435613854e+00\t1.21189387380989e+00 3.11789524141941e-01 3.11482609751535e-01 1.77213351675737e+00\n1.57134091523118e+00\t9.82417460447613e-01 4.43966298442428e-01 4.47248875541550e-01 1.06609857006796e+00\t1.21403302990330e+00 3.10399813134199e-01 3.09953202019350e-01 1.77929061083511e+00\n1.58311501194423e+00\t9.82417460447615e-01 4.41783065972825e-01 4.44958450576503e-01 1.06671484519588e+00\t1.21618498695457e+00 3.09007865188169e-01 3.08419050195858e-01 1.78649484528108e+00\n1.59493997534955e+00\t9.82417460447615e-01 4.39594339278999e-01 4.42658517122453e-01 1.06733320393379e+00\t1.21834953661975e+00 3.07613631517505e-01 3.06880051183123e-01 1.79374587971442e+00\n1.60681400441807e+00\t9.82417460447614e-01 4.37400658902706e-01 4.40349605326807e-01 1.06795364562544e+00\t1.22052644087105e+00 3.06217132927304e-01 3.05336090760082e-01 1.80104325098361e+00\n1.61873493753393e+00\t9.82417460447613e-01 4.35202580526343e-01 4.38032339040167e-01 1.06857612293451e+00\t1.22271549938886e+00 3.04818422207588e-01 3.03787230509555e-01 1.80838644640833e+00\n1.63070032603687e+00\t9.82417460447615e-01 4.33000755946734e-01 4.35707301375055e-01 1.06920054795929e+00\t1.22491646820112e+00 3.03417744506139e-01 3.02233549544173e-01 1.81577475928355e+00\n1.64269822459200e+00\t9.82417460447614e-01 4.30797594876014e-01 4.33376810725920e-01 1.06982622949423e+00\t1.22712734866425e+00 3.02016800522297e-01 3.00676652625352e-01 1.82320144067732e+00\n1.65471748698574e+00\t9.82417460447615e-01 4.28595241885066e-01 4.31043062104284e-01 1.07045275662875e+00\t1.22934600284906e+00 3.00616441697888e-01 2.99117378391035e-01 1.83065990628649e+00\n1.66675465192324e+00\t9.82417460447615e-01 4.26394408154968e-01 4.28706736036656e-01 1.07108007396006e+00\t1.23157170836978e+00 2.99216715279709e-01 2.97555769389387e-01 1.83814816177368e+00\n1.67880740362550e+00\t9.82417460447615e-01 4.24195542262435e-01 4.26368276498187e-01 1.07170811776676e+00\t1.23380392812877e+00 2.97817762406208e-01 2.95991931491838e-01 1.84566469491687e+00\n1.69087425948624e+00\t9.82417460447614e-01 4.21999014972589e-01 4.24027970971890e-01 1.07233682704217e+00\t1.23604237207424e+00 2.96419767604257e-01 2.94425785179802e-01 1.85320863563315e+00\n1.70295419206297e+00\t9.82417460447614e-01 4.19805204573071e-01 4.21686216728970e-01 1.07296621170462e+00\t1.23828677339328e+00 2.95022647659403e-01 2.92857108580362e-01 1.86077935948789e+00\n1.71504662988341e+00\t9.82417460447616e-01 4.17614374043902e-01 4.19343257336129e-01 1.07359631992590e+00\t1.24053693642941e+00 2.93626221372601e-01 2.91285629924984e-01 1.86837651174448e+00\n1.72715151183437e+00\t9.82417460447615e-01 4.15426604099585e-01 4.16999153607470e-01 1.07422721211016e+00\t1.24279272317856e+00 2.92230323612538e-01 2.89711061437935e-01 1.87599993023501e+00\n1.73926930589601e+00\t9.82417460447613e-01 4.13241901432984e-01 4.14653851725008e-01 1.07485893500835e+00\t1.24505417543937e+00 2.90834810388515e-01 2.88133128429107e-01 1.88365000613509e+00\n1.75140085099796e+00\t9.82417460447614e-01 4.11060292424143e-01 4.12307336215794e-01 1.07549153488748e+00\t1.24732133180714e+00 2.89439573066021e-01 2.86551579525221e-01 1.89132712199862e+00\n1.76354727094284e+00\t9.82417460447614e-01 4.08881997093024e-01 4.09959308164176e-01 1.07612516515690e+00\t1.24959399548423e+00 2.88044457777920e-01 2.84966097400107e-01 1.89903145727035e+00\n1.77571007079876e+00\t9.82417460447614e-01 4.06706785356883e-01 4.07609418889086e-01 1.07675988594969e+00\t1.25187251087144e+00 2.86648993698060e-01 2.83376216860984e-01 1.90676397117826e+00\n1.78789112381963e+00\t9.82417460447614e-01 4.04534305389851e-01 4.05257320298900e-01 1.07739580707783e+00\t1.25415708469030e+00 2.85252960766800e-01 2.81781492063212e-01 1.91452565720475e+00\n1.80009324908026e+00\t9.82417460447614e-01 4.02363601108160e-01 4.02903006228951e-01 1.07803298519562e+00\t1.25644892691477e+00 2.83856115878899e-01 2.80181909193450e-01 1.92231981603844e+00\n1.81232017470461e+00\t9.82417460447614e-01 4.00193501537428e-01 4.00546206687403e-01 1.07867166181082e+00\t1.25874814930808e+00 2.82458044017675e-01 2.78577481173886e-01 1.93014753535580e+00\n1.82457647449533e+00\t9.82417460447614e-01 3.98023449516980e-01 3.98185973031615e-01 1.07931212914692e+00\t1.26105528101003e+00 2.81058154539678e-01 2.76967475850711e-01 1.93801111500664e+00\n1.83686718254422e+00\t9.82417460447614e-01 3.95852819785578e-01 3.95821201866999e-01 1.07995469142256e+00\t1.26337148964558e+00 2.79655699077129e-01 2.75350887432275e-01 1.94591443408766e+00\n1.84919774810900e+00\t9.82417460447613e-01 3.93680938122624e-01 3.93450931683619e-01 1.08059972256547e+00\t1.26569807782612e+00 2.78249764442409e-01 2.73726738578191e-01 1.95386186263943e+00\n1.86157399604918e+00\t9.82417460447614e-01 3.91506933619097e-01 3.91074371977217e-01 1.08124754015799e+00\t1.26803614687385e+00 2.76839554584612e-01 2.72094364983125e-01 1.96185720028513e+00\n1.87400208938432e+00\t9.82417460447615e-01 3.89329970596572e-01 3.88690250461241e-01 1.08189848863306e+00\t1.27038660488379e+00 2.75424549529700e-01 2.70452437290516e-01 1.96990385222976e+00\n1.88648849009529e+00\t9.82417460447616e-01 3.87148820436812e-01 3.86297430309508e-01 1.08255291130233e+00\t1.27275086916507e+00 2.74004147552104e-01 2.68800228052996e-01 1.97800692808308e+00\n1.89903991769607e+00\t9.82417460447615e-01 3.84962383124573e-01 3.83894825451034e-01 1.08321119824114e+00\t1.27513061471401e+00 2.72577308813083e-01 2.67136634333872e-01 1.98617246452708e+00\n1.91166332464518e+00\t9.82417460447613e-01 3.82769539168801e-01 3.81481176622328e-01 1.08387372774214e+00\t1.27752758864220e+00 2.71143034358329e-01 2.65460530775903e-01 1.99440669621314e+00\n1.92436582627029e+00\t9.82417460447614e-01 3.80569242681777e-01 3.79055245887892e-01 1.08454095402619e+00\t1.27994299279186e+00 2.69700429937706e-01 2.63770487225905e-01 2.00271436664147e+00\n1.93715463865726e+00\t9.82417460447614e-01 3.78360435767030e-01 3.76615780329871e-01 1.08521335349763e+00\t1.28237808402893e+00 2.68248649423525e-01 2.62065011240359e-01 2.01110054248043e+00\n1.95003313337572e+00\t9.82417460447615e-01 3.76142860203671e-01 3.74162258150486e-01 1.08589123477875e+00\t1.28483334731310e+00 2.66787234405173e-01 2.60342849139230e-01 2.01956782151132e+00\n1.96299287005683e+00\t9.82417460447614e-01 3.73918173954020e-01 3.71696320341807e-01 1.08657428530271e+00\t1.28730704204139e+00 2.65317074319748e-01 2.58604387861884e-01 2.02811108808353e+00\n1.97603973707560e+00\t9.82417460447613e-01 3.71685271902101e-01 3.69216983599499e-01 1.08726275752866e+00\t1.28980045362571e+00 2.63837352080204e-01 2.56849204470341e-01 2.03673490044932e+00\n1.98917780198624e+00\t9.82417460447614e-01 3.69444091280805e-01 3.66723661816706e-01 1.08795684325191e+00\t1.29231404529310e+00 2.62347301349576e-01 2.55077246949636e-01 2.04544157553810e+00\n2.00240954667452e+00\t9.82417460447614e-01 3.67194246832024e-01 3.64216114056258e-01 1.08865665607339e+00\t1.29484848525255e+00 2.60846681066187e-01 2.53287958894104e-01 2.05423363679476e+00\n"
  },
  {
    "path": "tests/euler/amr-mass_conservation_02.mpirun=2.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\n[INFO] performing mesh adaptation\n[INFO] preparing compute kernels\n[INFO] performing mesh adaptation\n[INFO] preparing compute kernels\n[INFO] performing mesh adaptation\n[INFO] preparing compute kernels\n9.82417460447615e-01\n9.82417460447614e-01\n9.82417460447614e-01\n9.82417460447615e-01\n9.82417460447614e-01\n"
  },
  {
    "path": "tests/euler/amr-mass_conservation_02.mpirun=4.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\n[INFO] performing mesh adaptation\n[INFO] preparing compute kernels\n[INFO] performing mesh adaptation\n[INFO] preparing compute kernels\n[INFO] performing mesh adaptation\n[INFO] preparing compute kernels\n9.82417460447614e-01\n9.82417460447615e-01\n9.82417460447614e-01\n9.82417460447614e-01\n9.82417460447614e-01\n"
  },
  {
    "path": "tests/euler/amr-mass_conservation_02.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = check_mass\n\n  set enable mesh adaptivity    = true\n  set enable compute quantities = true\n\n  set final time                = 2.0\n  set timer granularity         = 0.5\n\n  set terminal update interval  = 0\n\n  set debug command             = ../../../../print_mass check_mass-interior-R0004-space_averaged_time_series.dat\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n  set gamma     = 1.4\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 4\n\n  subsection rectangular domain\n    set boundary condition bottom = slip\n    set boundary condition left   = slip\n    set boundary condition right  = slip\n    set boundary condition top    = slip\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.2\n  set cfl max            = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n\nsubsection I - MeshAdaptor\n  set adaptation strategy           = random adaptation\n  set time point selection strategy = fixed time points\n\n  subsection time point selection strategies\n    set fixed time points = 0.5, 1.0, 1.5\n  end\n\n  subsection marking strategies\n    set coarsening threshold = 0.80\n    set refinement threshold = 0.90\n  end\nend\n\nsubsection K - Quantities\n  set interior manifolds = interior : 0. : space_averaged\nend\n"
  },
  {
    "path": "tests/euler/amr-random_adaptation.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = check_random_refinement\n\n  set enable compute error      = true\n  set enable mesh adaptivity    = true\n\n  set final time                = 2.0\n  set timer granularity         = 0.5\n\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n  set gamma     = 1.4\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 4\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.2\n  set cfl max            = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n\nsubsection I - MeshAdaptor\n  set adaptation strategy           = random adaptation\n  set time point selection strategy = fixed time points\n\n  subsection time point selection strategies\n    set fixed time points = 0.5, 1.0, 1.5\n  end\n\n  subsection marking strategies\n    set coarsening threshold = 0.80\n    set refinement threshold = 0.90\n  end\nend\n"
  },
  {
    "path": "tests/euler/amr-random_adaptation.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\n[INFO] performing mesh adaptation\n[INFO] preparing compute kernels\n[INFO] performing mesh adaptation\n[INFO] preparing compute kernels\n[INFO] performing mesh adaptation\n[INFO] preparing compute kernels\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 800\nt     = 2.008229115565869\nLinf  = 0.5947862142865539\nL1    = 0.04457784279433662\nL2    = 0.08776597575422618\n"
  },
  {
    "path": "tests/euler/check-mass-conservation_01-dg.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = test\n\n  set enable compute quantities = true\n  set enable output full        = false\n\n  set final time                = 0.5\n  set timer granularity         = 0.5\n\n  set terminal update interval  = 0\n\n  set debug command             = ../../../print_mass test-interior-R0000-space_averaged_time_series.dat\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n  set gamma     = 1.4\nend\n\nsubsection C - Discretization\n  set finite element ansatz = dG Q1\n\n  set geometry              = rectangular domain\n  set mesh refinement       = 6\n  subsection rectangular domain\n    set boundary condition bottom = slip\n    set boundary condition left   = slip\n    set boundary condition right  = slip\n    set boundary condition top    = slip\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = uniform\n  set direction     =  1,  0\n  set position      =  0,  0\n\nend\n\nsubsection F - HyperbolicModule\n  subsection indicator\n    set evc factor = 0.125\n  end\n  subsection limiter\n    set relaxation factor     = 4.\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.9\n  set cfl max            = 0.9\n  set cfl recovery strategy = none\n  set time stepping scheme  = ssprk 33\nend\n\nsubsection K - Quantities\n  set interior manifolds           = interior : 0. : space_averaged\nend\n"
  },
  {
    "path": "tests/euler/check-mass-conservation_01-dg.threads=1.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\n1.40000000000000e+00\n1.39999999999999e+00\n1.40000000000000e+00\n1.39999999999999e+00\n1.40000000000000e+00\n"
  },
  {
    "path": "tests/euler/check-mass-conservation_01-simplex.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = test\n\n  set enable compute quantities = true\n  set enable output full        = false\n\n  set final time                = 0.5\n  set timer granularity         = 0.5\n\n  set terminal update interval  = 0\n\n  set debug filename            = test-interior-R0000-space_averaged_time_series.dat\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n  set gamma     = 1.4\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n  set mesh type       = parallel shared\n\n  subsection rectangular domain\n    set boundary condition bottom = slip\n    set boundary condition left   = slip\n    set boundary condition right  = slip\n    set boundary condition top    = slip\n\n    set simplex mesh              = true\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,    1\n  set position      =  0.5,  0.5\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.9\n  set cfl max            = 0.9\n  set cfl recovery strategy = none\n  set time stepping scheme  = ssprk 33\nend\n\nsubsection K - Quantities\n  set interior manifolds           = interior : 0. : space_averaged\nend\n"
  },
  {
    "path": "tests/euler/check-mass-conservation_01-simplex.threads=1.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\n# time t\tprimitive state (rho, v_1, v_2, p)\t and 2nd moments\n0.00000000000000e+00\t9.97542026659741e-01 1.38753721526887e+00 1.39669573565320e+00 1.00920523860197e+00\t9.95859927323039e-01 1.95833721822893e+00 1.98424142570653e+00 1.02447315313226e+00\n2.05996312183815e-02\t9.97542026659749e-01 1.38587777976308e+00 1.39498956853105e+00 1.01113985072081e+00\t9.96432023708764e-01 1.95383128770921e+00 1.97938332135545e+00 1.03009102639644e+00\n4.14944055983232e-02\t9.97542026659753e-01 1.38440918585900e+00 1.39349503486209e+00 1.01286625388677e+00\t9.97821497214417e-01 1.94995945158329e+00 1.97527515997745e+00 1.03814940972189e+00\n6.26771524762082e-02\t9.97542026659719e-01 1.38300266171342e+00 1.39206909748704e+00 1.01454136568827e+00\t9.99987520886347e-01 1.94632919817120e+00 1.97144718001821e+00 1.04885081212666e+00\n8.41320844782112e-02\t9.97542026659735e-01 1.38157315123803e+00 1.39061152598222e+00 1.01626200458342e+00\t1.00289867076585e+00 1.94270058767374e+00 1.96760085891727e+00 1.06235859507749e+00\n1.05884279519714e-01\t9.97542026659730e-01 1.37992753206598e+00 1.38889655634905e+00 1.01821458508716e+00\t1.00652384224996e+00 1.93859184769558e+00 1.96315476339475e+00 1.07901349669051e+00\n1.27956060491108e-01\t9.97542026659732e-01 1.37793618377396e+00 1.38680431779765e+00 1.02047798338331e+00\t1.01080662174007e+00 1.93373287570316e+00 1.95786824576282e+00 1.09884821240278e+00\n1.50367469098215e-01\t9.97542026659739e-01 1.37538795725216e+00 1.38413558756056e+00 1.02320366204901e+00\t1.01554289534418e+00 1.92771695397255e+00 1.95137564021122e+00 1.12148306681476e+00\n1.73121271238196e-01\t9.97542026659729e-01 1.37236988744207e+00 1.38098560685177e+00 1.02624433033005e+00\t1.02060983757969e+00 1.92087973625077e+00 1.94403748660833e+00 1.14623611744780e+00\n1.96204294288132e-01\t9.97542026659741e-01 1.36893162516381e+00 1.37741003629870e+00 1.02950972522531e+00\t1.02588272560352e+00 1.91345573568157e+00 1.93610467438044e+00 1.17251920856482e+00\n2.19605113216834e-01\t9.97542026659747e-01 1.36519092628310e+00 1.37352962882848e+00 1.03286831269343e+00\t1.03135764765321e+00 1.90577665407261e+00 1.92791513022362e+00 1.20010558202341e+00\n2.43310442866879e-01\t9.97542026659751e-01 1.36125383372467e+00 1.36944819574115e+00 1.03622845662204e+00\t1.03707630131680e+00 1.89807493288009e+00 1.91969678558459e+00 1.22900804801900e+00\n2.67299731150706e-01\t9.97542026659748e-01 1.35717869092757e+00 1.36522534616973e+00 1.03955357385289e+00\t1.04305774024709e+00 1.89041446990955e+00 1.91151938975761e+00 1.25916293591748e+00\n2.91455260254429e-01\t9.97542026659750e-01 1.35302346101936e+00 1.36091934272212e+00 1.04281928111015e+00\t1.04929690106462e+00 1.88283925584858e+00 1.90342868716714e+00 1.29045770428510e+00\n3.15643020879122e-01\t9.97542026659744e-01 1.34885102772514e+00 1.35659767428852e+00 1.04600690919882e+00\t1.05577556791654e+00 1.87541150791224e+00 1.89549604375672e+00 1.32274586621466e+00\n3.39897368364945e-01\t9.97542026659740e-01 1.34465244503865e+00 1.35225149590253e+00 1.04913431038853e+00\t1.06250743995196e+00 1.86808565964226e+00 1.88767667519673e+00 1.35609811718483e+00\n3.64267046053730e-01\t9.97542026659743e-01 1.34040534326272e+00 1.34785983301965e+00 1.05221972899735e+00\t1.06950216621174e+00 1.86080885321498e+00 1.87992044018693e+00 1.39056247970328e+00\n3.88805439828434e-01\t9.97542026659741e-01 1.33611799893475e+00 1.34343445453431e+00 1.05527581880493e+00\t1.07676365682499e+00 1.85356600254760e+00 1.87221526357196e+00 1.42613381208377e+00\n4.13487034228607e-01\t9.97542026659750e-01 1.33181580793395e+00 1.33900360234626e+00 1.05830615156618e+00\t1.08426729272610e+00 1.84636431673055e+00 1.86457037326490e+00 1.46267192780160e+00\n4.38261200049320e-01\t9.97542026659739e-01 1.32751541186699e+00 1.33458230824915e+00 1.06130847904843e+00\t1.09199439625596e+00 1.83921362777438e+00 1.85699636807947e+00 1.50011095747583e+00\n4.63170871721581e-01\t9.97542026659746e-01 1.32320846484248e+00 1.33015731958198e+00 1.06428945212829e+00\t1.09996979218631e+00 1.83210476204944e+00 1.84947948257212e+00 1.53867749095838e+00\n4.88209613433615e-01\t9.97542026659731e-01 1.31887067020040e+00 1.32570455600176e+00 1.06728057147712e+00\t1.10808393626103e+00 1.82495814580761e+00 1.84194169433198e+00 1.57792359505509e+00\n5.13389375134314e-01\t9.97542026659740e-01 1.31447463947649e+00 1.32119520794761e+00 1.07030689925341e+00\t1.11624889295614e+00 1.81770066816815e+00 1.83430894044589e+00 1.61754549270748e+00\n"
  },
  {
    "path": "tests/euler/check-mass-conservation_01.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = test\n\n  set enable compute quantities = true\n  set enable output full        = false\n\n  set final time                = 0.5\n  set timer granularity         = 0.5\n\n  set terminal update interval  = 0\n\n  set debug filename            = test-interior-R0000-space_averaged_time_series.dat\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n  set gamma     = 1.4\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n  subsection rectangular domain\n    set boundary condition bottom = slip\n    set boundary condition left   = slip\n    set boundary condition right  = slip\n    set boundary condition top    = slip\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = uniform\n  set direction     =  1,  0\n  set position      =  0,  0\n\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.9\n  set cfl max            = 0.9\n  set cfl recovery strategy = none\n  set time stepping scheme  = ssprk 33\nend\n\nsubsection K - Quantities\n  set interior manifolds           = interior : 0. : space_averaged\nend\n"
  },
  {
    "path": "tests/euler/check-mass-conservation_01.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\n# time t\tprimitive state (rho, v_1, v_2, p)\t and 2nd moments\n0.00000000000000e+00\t1.40000000000000e+00 2.95312500000000e+00 0.00000000000000e+00 1.03937500000000e+00\t1.96000000000000e+00 8.85937500000000e+00 0.00000000000000e+00 1.17797500000000e+00\n2.50437550648706e-02\t1.40000000000000e+00 2.94696583136351e+00 8.88300392444530e-19 1.05012196147440e+00\t1.96265579282553e+00 8.82350158903013e+00 4.79703068611312e-10 1.22533246562805e+00\n5.03546786069546e-02\t1.39999999999999e+00 2.94139975680155e+00 1.33133250517642e-18 1.06051992333594e+00\t1.96999117443584e+00 8.79255724603172e+00 7.44998122237302e-09 1.31647312486851e+00\n7.60766238558612e-02\t1.40000000000000e+00 2.93530673365166e+00 1.94275476782246e-18 1.07190332538500e+00\t1.98142239699515e+00 8.75998065381497e+00 3.33838870483115e-08 1.44953523794477e+00\n1.02416335400984e-01\t1.39999999999999e+00 2.92784595353807e+00 1.91375235970848e-18 1.08498564346673e+00\t1.99631316745490e+00 8.72197753541423e+00 9.47940140842022e-08 1.62026260761714e+00\n1.29647815936606e-01\t1.40000000000001e+00 2.91882101292658e+00 2.36342521074684e-18 1.09959192159423e+00\t2.01403762457260e+00 8.67861290091190e+00 2.26739769517138e-07 1.82391472356685e+00\n1.58114776774656e-01\t1.40000000000001e+00 2.90856965812043e+00 2.93832341270728e-18 1.11508776412152e+00\t2.03458801155435e+00 8.63225817423134e+00 5.05356635079022e-07 2.06120575583795e+00\n1.86844653436458e-01\t1.40000000000001e+00 2.89801759915227e+00 3.26046698320703e-18 1.13068343005390e+00\t2.05703321278737e+00 8.58686051952774e+00 1.01923675995271e-06 2.32197199967073e+00\n2.15599136556393e-01\t1.39999999999999e+00 2.88729533238315e+00 3.94591992544310e-18 1.14621130856816e+00\t2.08106733783169e+00 8.54252241811188e+00 1.82090715997836e-06 2.60103565465484e+00\n2.44352536971706e-01\t1.39999999999999e+00 2.87625534279262e+00 4.33396265923924e-18 1.16158865018795e+00\t2.10668487472367e+00 8.49807669988317e+00 2.97457525499639e-06 2.89585738033153e+00\n2.73116989368986e-01\t1.40000000000001e+00 2.86495502018746e+00 5.19697064787876e-18 1.17681157812916e+00\t2.13381322962669e+00 8.45305510424714e+00 4.54300290657729e-06 3.20485373742925e+00\n3.01885486125323e-01\t1.40000000000001e+00 2.85350924975574e+00 5.50639178351076e-18 1.19198109719048e+00\t2.16218310337640e+00 8.40749702048710e+00 6.52777031725824e-06 3.52439444544509e+00\n3.30647248613562e-01\t1.39999999999999e+00 2.84212247196659e+00 6.24808771344451e-18 1.20713152693550e+00\t2.19160938738226e+00 8.36196767590956e+00 8.80673609147497e-06 3.85226853660754e+00\n3.59403721469343e-01\t1.40000000000000e+00 2.83085149784582e+00 7.28948084077578e-18 1.22218956471091e+00\t2.22215009743937e+00 8.31690069409932e+00 1.14016349141102e-05 4.18909269470038e+00\n3.88164433543616e-01\t1.40000000000000e+00 2.81964188352471e+00 7.57362651326171e-18 1.23708283128957e+00\t2.25394917802217e+00 8.27237919519431e+00 1.43621992670692e-05 4.53639095840783e+00\n4.16928158386954e-01\t1.40000000000000e+00 2.80846665143408e+00 8.19556892511265e-18 1.25180394980684e+00\t2.28703484397413e+00 8.22838201226431e+00 1.68106882542547e-05 4.89447862034785e+00\n4.45687276111425e-01\t1.40000000000001e+00 2.79729327447594e+00 8.97841371562402e-18 1.26637477717117e+00\t2.32144279489947e+00 8.18479346960883e+00 1.82795967775686e-05 5.26356001385216e+00\n4.74443497541669e-01\t1.39999999999999e+00 2.78602111436110e+00 9.92310956169674e-18 1.28079375631684e+00\t2.35725321052496e+00 8.14131418238158e+00 1.91389811220965e-05 5.64404886473615e+00\n5.03198852844000e-01\t1.39999999999999e+00 2.77462213780313e+00 1.11057708439711e-17 1.29507949788396e+00\t2.39442155382440e+00 8.09779522784373e+00 1.96791875685889e-05 6.03518896172286e+00\n"
  },
  {
    "path": "tests/euler/check-mass-conservation_02-dg.mpirun=1.threads=1.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\n1.39999999999991e+00\n1.39999999999991e+00\n1.39999999999992e+00\n1.39999999999991e+00\n1.39999999999990e+00\n"
  },
  {
    "path": "tests/euler/check-mass-conservation_02-dg.mpirun=4.threads=1.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\n1.39999999999991e+00\n1.39999999999991e+00\n1.39999999999991e+00\n1.39999999999991e+00\n1.39999999999991e+00\n"
  },
  {
    "path": "tests/euler/check-mass-conservation_02-dg.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = test\n\n  set enable compute quantities = true\n  set enable output full        = false\n\n  set final time                = 0.50\n  set timer granularity         = 0.50\n\n  set terminal update interval  = 0\n\n  set debug command             = ../../../../print_mass test-interior-R0000-space_averaged_time_series.dat\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n  set gamma     = 1.4\nend\n\nsubsection C - Discretization\n  set finite element ansatz = dG Q1\n\n  set geometry        = annulus\n  set mesh refinement = 0\nend\n\nsubsection E - InitialValues\n  set configuration = uniform\n  set direction     =  1,  0\n  set position      =  0,  0\n\nend\n\nsubsection F - HyperbolicModule\n  subsection indicator\n    set evc factor = 0.125\n  end\n  subsection limiter\n    set relaxation factor     = 4.\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.9\n  set cfl max            = 0.9\n  set cfl recovery strategy = none\n  set time stepping scheme  = ssprk 33\nend\n\nsubsection K - Quantities\n  set interior manifolds           = interior : 0. : space_averaged\nend\n"
  },
  {
    "path": "tests/euler/check-mass-conservation_02.mpirun=1.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\n# time t\tprimitive state (rho, v_1, v_2, p)\t and 2nd moments\n0.00000000000000e+00\t1.40000000000000e+00 2.92532255614329e+00 4.68952251502315e-15 1.06272905283963e+00\t1.96000000000000e+00 8.75640448742187e+00 1.95631810080187e-02 1.26973153831588e+00\n1.09221806369784e-03\t1.40000000000000e+00 2.91983353850375e+00 4.04898627123958e-15 1.07219495142293e+00\t1.96151241613506e+00 8.72408650988086e+00 1.95286638716861e-02 1.30225762375569e+00\n2.18443612739568e-03\t1.40000000000000e+00 2.91523847229735e+00 3.41999311690542e-15 1.08057930813620e+00\t1.96580873970098e+00 8.69776531028807e+00 1.95360220473170e-02 1.35617002317428e+00\n3.27665419109352e-03\t1.40000000000000e+00 2.91115461442755e+00 2.90517458325232e-15 1.08850936508295e+00\t1.97262511965046e+00 8.67479860980968e+00 1.95849344877393e-02 1.43035751266704e+00\n4.36887225479136e-03\t1.40000000000000e+00 2.90725320381727e+00 2.50862981463802e-15 1.09645880870976e+00\t1.98177417759209e+00 8.65318025994666e+00 1.96668746795708e-02 1.52449411904329e+00\n5.46109031848918e-03\t1.40000000000000e+00 2.90307928333564e+00 1.84624079931763e-15 1.10498067489609e+00\t1.99302864698503e+00 8.63045488464150e+00 1.97887597685431e-02 1.63822791357328e+00\n6.55330838218692e-03\t1.39999999999999e+00 2.89843998577302e+00 9.14033836123704e-16 1.11419230813543e+00\t2.00616820006443e+00 8.60571881436077e+00 1.99497227364788e-02 1.77028507759275e+00\n7.64552644588452e-03\t1.40000000000000e+00 2.89321641668757e+00 -8.60192071513680e-18 1.12411192230938e+00\t2.02091786009182e+00 8.57854232188140e+00 2.01617124372788e-02 1.91843286344397e+00\n8.73774450958249e-03\t1.40000000000000e+00 2.88738088344061e+00 -8.24963382918075e-16 1.13463688991174e+00\t2.03701382091157e+00 8.54900245325415e+00 2.04237863965961e-02 2.08034149607510e+00\n9.82996257328433e-03\t1.40000000000000e+00 2.88096082858840e+00 -1.72760035943180e-15 1.14561221943545e+00\t2.05425135870626e+00 8.51743237940267e+00 2.07348816006266e-02 2.25423064731769e+00\n1.09221806369989e-02\t1.40000000000000e+00 2.87398314731094e+00 -3.08829973491284e-15 1.15691513939758e+00\t2.07246115767488e+00 8.48413355931725e+00 2.11204200028435e-02 2.43851695305198e+00\n1.20143987006684e-02\t1.39999999999999e+00 2.86644326619590e+00 -4.54617751649558e-15 1.16847956223352e+00\t2.09140809231457e+00 8.44925238644728e+00 2.16182937548622e-02 2.63120110783961e+00\n1.31066167624060e-02\t1.39999999999999e+00 2.85834977140757e+00 -6.82016696015553e-15 1.18023523429739e+00\t2.11090056140262e+00 8.41300885356899e+00 2.22565481734991e-02 2.83043774602287e+00\n1.41988348153087e-02\t1.40000000000000e+00 2.84976825311536e+00 -9.84389514675412e-15 1.19211261671955e+00\t2.13086781835539e+00 8.37577748615669e+00 2.30407097301951e-02 3.03526218377305e+00\n1.52910528414613e-02\t1.40000000000000e+00 2.84080905466764e+00 -1.20622976463642e-14 1.20405507044548e+00\t2.15127691886638e+00 8.33796751283074e+00 2.39498218605720e-02 3.24502242227166e+00\n1.63832707990159e-02\t1.40000000000000e+00 2.83159331301629e+00 -1.25717577470958e-14 1.21601671318494e+00\t2.17215200132534e+00 8.29987689269104e+00 2.49534146895119e-02 3.45952542339261e+00\n1.74754885946034e-02\t1.39999999999999e+00 2.82221365596653e+00 -1.16750207190225e-14 1.22796303588995e+00\t2.19354759623171e+00 8.26160829165837e+00 2.60224683840346e-02 3.67897430274100e+00\n1.85677060471345e-02\t1.39999999999999e+00 2.81270078757861e+00 -8.41467853964412e-15 1.23989669079591e+00\t2.21543677265457e+00 8.22306259207545e+00 2.71427557514641e-02 3.90297998644596e+00\n1.96599228487456e-02\t1.40000000000000e+00 2.80308659627360e+00 -5.26815610622055e-15 1.25180875223537e+00\t2.23780529749878e+00 8.18428743642117e+00 2.83148775186803e-02 4.13128286684109e+00\n2.07521385096352e-02\t1.40000000000000e+00 2.79341795311328e+00 -2.19252404060441e-15 1.26368252107720e+00\t2.26065212620518e+00 8.14546423738671e+00 2.95451706841969e-02 4.36379225500083e+00\n2.18443522980992e-02\t1.40000000000000e+00 2.78376072242567e+00 1.23447077224708e-15 1.27550399647840e+00\t2.28399199945667e+00 8.10682749488648e+00 3.08415665457446e-02 4.60060964700225e+00\n2.29365631790331e-02\t1.40000000000000e+00 2.77421086463468e+00 5.12371165689032e-15 1.28725678592171e+00\t2.30786118803558e+00 8.06861296408760e+00 3.22079199879489e-02 4.84198824294550e+00\n2.40287697568433e-02\t1.40000000000000e+00 2.76478737647148e+00 7.31678385561052e-15 1.29892802784337e+00\t2.33229588421798e+00 8.03084907826007e+00 3.36390255854997e-02 5.08818959086195e+00\n2.51209702405686e-02\t1.40000000000000e+00 2.75550310336827e+00 4.31843116929718e-15 1.31051079465853e+00\t2.35732567468171e+00 7.99353859803861e+00 3.51121492028510e-02 5.33931271394211e+00\n2.62131624647695e-02\t1.40000000000000e+00 2.74638335774208e+00 -1.94256041340958e-15 1.32200606954338e+00\t2.38295962652776e+00 7.95668499362610e+00 3.66089021137263e-02 5.59528402405738e+00\n2.73053439794887e-02\t1.40000000000000e+00 2.73737582793879e+00 -1.16292425475305e-14 1.33343382672261e+00\t2.40914806466601e+00 7.92009749992447e+00 3.81229914522964e-02 5.85540201944990e+00\n2.83975122627515e-02\t1.40000000000000e+00 2.72845019499848e+00 -1.89096931420775e-14 1.34481699242852e+00\t2.43582005614833e+00 7.88366003531261e+00 3.96450355832264e-02 6.11888211485464e+00\n2.94896650267403e-02\t1.40000000000000e+00 2.71958087220002e+00 -2.37902641814253e-14 1.35617007821276e+00\t2.46292009975181e+00 7.84731007116473e+00 4.11754603506091e-02 6.38511831272273e+00\n3.05818004597824e-02\t1.40000000000000e+00 2.71073524286044e+00 -2.72183005114891e-14 1.36750113852156e+00\t2.49039918514208e+00 7.81103898023868e+00 4.27222696512089e-02 6.65363219147805e+00\n3.16739176112855e-02\t1.39999999999999e+00 2.70190866994653e+00 -2.94487775467827e-14 1.37882186229049e+00\t2.51819515102794e+00 7.77485048832383e+00 4.42957833704967e-02 6.92381228444696e+00\n3.27660167953439e-02\t1.40000000000000e+00 2.69311678163622e+00 -3.12484445078222e-14 1.39013313117255e+00\t2.54627430018446e+00 7.73876497813773e+00 4.59013128893656e-02 7.19528155436703e+00\n3.38580992987475e-02\t1.40000000000000e+00 2.68432969894739e+00 -3.17478761641360e-14 1.40143629311065e+00\t2.57459548973751e+00 7.70268566294369e+00 4.75457478066254e-02 7.46752340272779e+00\n3.49501672600550e-02\t1.39999999999999e+00 2.67550880713367e+00 -3.20372794682988e-14 1.41273576746726e+00\t2.60310088731407e+00 7.66652840440592e+00 4.92293297676565e-02 7.73985736511021e+00\n3.60422236396977e-02\t1.39999999999999e+00 2.66663909162248e+00 -3.37876114152740e-14 1.42403535635896e+00\t2.63173815788447e+00 7.63031478095329e+00 5.09514277465051e-02 8.01179458206536e+00\n3.71342710896857e-02\t1.40000000000000e+00 2.65771276164127e+00 -3.61763569740340e-14 1.43533509721537e+00\t2.66048511452436e+00 7.59405903726165e+00 5.27114119351288e-02 8.28326212213599e+00\n3.82263117717790e-02\t1.39999999999999e+00 2.64870615181624e+00 -3.83537563174063e-14 1.44666583849068e+00\t2.68919461748958e+00 7.55770190196511e+00 5.44994851284091e-02 8.55273682709598e+00\n3.93183473095250e-02\t1.39999999999999e+00 2.63962166388283e+00 -3.78695331521263e-14 1.45804198118119e+00\t2.71777930992373e+00 7.52125283295277e+00 5.63114347179903e-02 8.81946151191348e+00\n4.04103788090165e-02\t1.40000000000000e+00 2.63046002064831e+00 -3.97418885772903e-14 1.46946089214849e+00\t2.74622622894440e+00 7.48474117935994e+00 5.81459826096131e-02 9.08357436797812e+00\n4.15024068231954e-02\t1.40000000000000e+00 2.62128856220370e+00 -4.21946617803095e-14 1.48091260214748e+00\t2.77459855952484e+00 7.44826070025153e+00 5.99958736748150e-02 9.34586540339853e+00\n4.25944315115556e-02\t1.40000000000000e+00 2.61209779997025e+00 -4.40141629636898e-14 1.49239757626498e+00\t2.80290715157843e+00 7.41173772438235e+00 6.18649828472972e-02 9.60672849808278e+00\n4.36864528904850e-02\t1.39999999999999e+00 2.60286457966413e+00 -4.35021263015135e-14 1.50392394366680e+00\t2.83110399308741e+00 7.37511130902069e+00 6.37481806127702e-02 9.86592814328744e+00\n4.47784709065568e-02\t1.39999999999999e+00 2.59358173659717e+00 -4.57673061731185e-14 1.51547723519755e+00\t2.85925307011424e+00 7.33840629523612e+00 6.56467178973862e-02 1.01242566441523e+01\n4.58704855229492e-02\t1.40000000000000e+00 2.58426304570085e+00 -4.78017162358051e-14 1.52703869712955e+00\t2.88746509281324e+00 7.30167359907711e+00 6.75604707129033e-02 1.03830071663591e+01\n4.69624967861221e-02\t1.40000000000000e+00 2.57491388027234e+00 -5.05436634715032e-14 1.53860434311155e+00\t2.91577488767129e+00 7.26493406446184e+00 6.94830786338024e-02 1.06426311482041e+01\n4.80545048755593e-02\t1.39999999999999e+00 2.56553092713936e+00 -4.93507030402880e-14 1.55016911265864e+00\t2.94419818390528e+00 7.22821502448321e+00 7.14137010650144e-02 1.09032455013891e+01\n4.91465100926524e-02\t1.40000000000000e+00 2.55612296715626e+00 -4.96833383486425e-14 1.56173009259960e+00\t2.97274966725719e+00 7.19154177719986e+00 7.33521418548995e-02 1.11648807105534e+01\n5.02385129532777e-02\t1.40000000000000e+00 2.54669818190331e+00 -5.08606312980602e-14 1.57328605305076e+00\t3.00142740032748e+00 7.15492071932553e+00 7.52935598254921e-02 1.14273712937313e+01\n5.13305141062964e-02\t1.40000000000000e+00 2.53724441211260e+00 -5.27970045159743e-14 1.58482484106262e+00\t3.03024857127090e+00 7.11833713308179e+00 7.72366449342359e-02 1.16907711594553e+01\n5.24225142328599e-02\t1.40000000000000e+00 2.52773534051696e+00 -5.37286868058466e-14 1.59633702637011e+00\t3.05921132168538e+00 7.08174443267471e+00 7.91828468466765e-02 1.19551268608333e+01\n5.35145140107350e-02\t1.39999999999999e+00 2.51817699974945e+00 -5.57030993870778e-14 1.60782153889627e+00\t3.08824500743069e+00 7.04517944530357e+00 8.11381614141070e-02 1.22197596294117e+01\n5.46065140069523e-02\t1.40000000000000e+00 2.50858976924187e+00 -5.84156335990423e-14 1.61927859073317e+00\t3.11727782637423e+00 7.00869764594194e+00 8.31053190950158e-02 1.24840415876327e+01\n5.56985147043522e-02\t1.40000000000000e+00 2.49898221831797e+00 -6.04610898901782e-14 1.63069861248567e+00\t3.14628910130038e+00 6.97232346296077e+00 8.50813653342872e-02 1.27477567843977e+01\n5.67905163579325e-02\t1.40000000000000e+00 2.48936169350376e+00 -6.25411088575190e-14 1.64207968354365e+00\t3.17523889313279e+00 6.93606792087996e+00 8.70598733192216e-02 1.30105182814035e+01\n5.78825188863036e-02\t1.40000000000000e+00 2.47970949349694e+00 -6.55930597984493e-14 1.65342638982647e+00\t3.20401597098689e+00 6.89986412826052e+00 8.90423001477499e-02 1.32711983002777e+01\n5.89745219143563e-02\t1.39999999999999e+00 2.47003314326704e+00 -6.68838009352011e-14 1.66472120060063e+00\t3.23260759773082e+00 6.86375860418125e+00 9.10168901562637e-02 1.35297393055176e+01\n6.00665248429860e-02\t1.40000000000000e+00 2.46034958175066e+00 -6.70908509970401e-14 1.67595152505151e+00\t3.26099434818405e+00 6.82781454630048e+00 9.29972622917141e-02 1.37860182976253e+01\n6.11585267816704e-02\t1.40000000000000e+00 2.45063676449302e+00 -6.47482767745433e-14 1.68712849217725e+00\t3.28899298679339e+00 6.79197806892336e+00 9.49875992575034e-02 1.40382875978779e+01\n6.22505266180332e-02\t1.40000000000000e+00 2.44091190644951e+00 -6.18357909608248e-14 1.69823200481118e+00\t3.31659439468920e+00 6.75632303163397e+00 9.69969512295236e-02 1.42863435959992e+01\n6.33425231796506e-02\t1.40000000000000e+00 2.43118166234531e+00 -6.05121744426951e-14 1.70925009443446e+00\t3.34377527018951e+00 6.72086365090386e+00 9.90309899712732e-02 1.45298567449866e+01\n6.44345153438511e-02\t1.39999999999999e+00 2.42143449852786e+00 -6.20071463185068e-14 1.72017811850218e+00\t3.37047403159064e+00 6.68557062610917e+00 1.01089040498972e-01 1.47682736103754e+01\n6.55265021681975e-02\t1.39999999999999e+00 2.41167021644092e+00 -6.32050719102963e-14 1.73101259955044e+00\t3.39668151639087e+00 6.65042919673580e+00 1.03159008531380e-01 1.50014904109586e+01\n6.66184831018491e-02\t1.39999999999999e+00 2.40188309504517e+00 -6.42246529465103e-14 1.74173709472454e+00\t3.42244290956882e+00 6.61543562244789e+00 1.05242894730918e-01 1.52301016940314e+01\n6.77104578295083e-02\t1.39999999999999e+00 2.39205732933811e+00 -6.53955137560932e-14 1.75235301210219e+00\t3.44765077022563e+00 6.58058238443404e+00 1.07337169016903e-01 1.54530471693687e+01\n6.88024262172413e-02\t1.40000000000000e+00 2.38218073391314e+00 -6.78362949857920e-14 1.76286182740164e+00\t3.47225259499607e+00 6.54583707285833e+00 1.09440496257298e-01 1.56698247795876e+01\n6.98943883084647e-02\t1.40000000000000e+00 2.37225741775271e+00 -6.82730643274453e-14 1.77325393616310e+00\t3.49625571698648e+00 6.51119180875852e+00 1.11557495631965e-01 1.58804497408943e+01\n7.09863442521569e-02\t1.39999999999999e+00 2.36230153714792e+00 -6.86850508546307e-14 1.78351239531559e+00\t3.51968250980171e+00 6.47670970346948e+00 1.13694806390062e-01 1.60850388555907e+01\n7.20782941949427e-02\t1.39999999999999e+00 2.35232824392294e+00 -6.63229885559984e-14 1.79362214847459e+00\t3.54256276052410e+00 6.44248509843539e+00 1.15847387780636e-01 1.62838348332321e+01\n7.31702382168085e-02\t1.39999999999999e+00 2.34235074351811e+00 -6.71605960989302e-14 1.80357735742798e+00\t3.56489817304628e+00 6.40858352506808e+00 1.18010666603448e-01 1.64769723214398e+01\n7.42621763730543e-02\t1.40000000000000e+00 2.33234613070206e+00 -6.76919112687762e-14 1.81338686163505e+00\t3.58658820965784e+00 6.37496319640146e+00 1.20188604474233e-01 1.66634844580546e+01\n7.53541096373066e-02\t1.39999999999999e+00 2.32234234946361e+00 -6.75397824744434e-14 1.82302859204434e+00\t3.60772353123541e+00 6.34173828979593e+00 1.22386707138596e-01 1.68442852706009e+01\n7.64460378219027e-02\t1.39999999999998e+00 2.31231745447637e+00 -6.94647100299948e-14 1.83251101971657e+00\t3.62816597827537e+00 6.30885717162428e+00 1.24608599762942e-01 1.70180874072002e+01\n7.75379605508271e-02\t1.39999999999999e+00 2.30228553217033e+00 -6.99238620002684e-14 1.84180434930735e+00\t3.64802518843065e+00 6.27641115087126e+00 1.26857421492512e-01 1.71859709851846e+01\n7.86298775183363e-02\t1.40000000000000e+00 2.29224179330586e+00 -6.87229737910223e-14 1.85090398163490e+00\t3.66725269134135e+00 6.24440044374072e+00 1.29125250699194e-01 1.73475035059539e+01\n7.97217890620799e-02\t1.39999999999999e+00 2.28219816801449e+00 -6.86994806286516e-14 1.85981836764881e+00\t3.68576970733051e+00 6.21287772893870e+00 1.31410151222070e-01 1.75022044952297e+01\n8.08136959525806e-02\t1.39999999999999e+00 2.27214348747646e+00 -6.90341166889403e-14 1.86855756802020e+00\t3.70347281825593e+00 6.18183651296530e+00 1.33719289819614e-01 1.76492099742712e+01\n8.19055991983537e-02\t1.39999999999999e+00 2.26205756497666e+00 -7.03555300514039e-14 1.87710894594367e+00\t3.72045078915794e+00 6.15126039072752e+00 1.36055092896503e-01 1.77892863042959e+01\n8.29974999902534e-02\t1.39999999999999e+00 2.25192485465930e+00 -7.30103003876980e-14 1.88547201162260e+00\t3.73678019094628e+00 6.12111283696540e+00 1.38423107619852e-01 1.79231913105963e+01\n8.40893996532543e-02\t1.39999999999999e+00 2.24175113699939e+00 -7.45873514547628e-14 1.89364196188390e+00\t3.75251733740222e+00 6.09141379488809e+00 1.40828501812474e-01 1.80514480386229e+01\n8.51812996014562e-02\t1.39999999999999e+00 2.23153287596138e+00 -7.66803851611846e-14 1.90162852705619e+00\t3.76761234134749e+00 6.06213208896085e+00 1.43274002923327e-01 1.81737119509728e+01\n8.62732013058429e-02\t1.39999999999999e+00 2.22126832104406e+00 -7.82280321604331e-14 1.90943301886269e+00\t3.78211097855502e+00 6.03325546927030e+00 1.45752983373334e-01 1.82904264901414e+01\n8.73651071431361e-02\t1.39999999999999e+00 2.21097636932567e+00 -7.88362320894741e-14 1.91707787663996e+00\t3.79586090904945e+00 6.00482131755256e+00 1.48257324236242e-01 1.84002374004761e+01\n8.84570197095887e-02\t1.39999999999999e+00 2.20065453085057e+00 -8.31485851015694e-14 1.92455613766903e+00\t3.80903942330543e+00 5.97680302488652e+00 1.50792810086476e-01 1.85049726724490e+01\n8.95489414516615e-02\t1.39999999999999e+00 2.19030081823491e+00 -9.07573318625189e-14 1.93186618589322e+00\t3.82178166549485e+00 5.94919653300155e+00 1.53367452779904e-01 1.86057133536237e+01\n9.06408745806657e-02\t1.39999999999999e+00 2.17989808517625e+00 -9.72707770973139e-14 1.93902413535728e+00\t3.83408571062968e+00 5.92198561628393e+00 1.55985311339401e-01 1.87025343558424e+01\n9.17328210910194e-02\t1.39999999999999e+00 2.16944067411467e+00 -9.83340665925223e-14 1.94603770776132e+00\t3.84579624819822e+00 5.89521319688785e+00 1.58645851299184e-01 1.87942673086239e+01\n9.28247831895557e-02\t1.40000000000000e+00 2.15895274406101e+00 -1.02434673059725e-13 1.95291561887316e+00\t3.85688561404668e+00 5.86894215982863e+00 1.61345619250323e-01 1.88808507956124e+01\n9.39167612356229e-02\t1.40000000000000e+00 2.14845672055865e+00 -1.06362122238676e-13 1.95965838286783e+00\t3.86750510876958e+00 5.84314744171665e+00 1.64077230204863e-01 1.89634812977393e+01\n9.50087533942885e-02\t1.39999999999999e+00 2.13792232307122e+00 -1.09013288713832e-13 1.96627212977485e+00\t3.87770988727502e+00 5.81772478655295e+00 1.66837232608657e-01 1.90427328289580e+01\n9.61007566822060e-02\t1.39999999999999e+00 2.12732180075661e+00 -1.10159144602197e-13 1.97276539727028e+00\t3.88739330975585e+00 5.79271403121707e+00 1.69631903589731e-01 1.91179034242205e+01\n9.71927682499051e-02\t1.39999999999999e+00 2.11665146167396e+00 -1.07997150332244e-13 1.97914123446782e+00\t3.89657128321013e+00 5.76822264662632e+00 1.72471914707155e-01 1.91894097964441e+01\n9.82847863738979e-02\t1.39999999999999e+00 2.10595815336512e+00 -1.07739678455771e-13 1.98539257006777e+00\t3.90540217973075e+00 5.74430301655387e+00 1.75356626339378e-01 1.92586205978980e+01\n9.93768086204802e-02\t1.40000000000000e+00 2.09521750303798e+00 -1.12218468235744e-13 1.99153255919884e+00\t3.91385272843015e+00 5.72075565457164e+00 1.78269507717260e-01 1.93252115764805e+01\n1.00468832102653e-01\t1.39999999999999e+00 2.08442704263360e+00 -1.03804548682108e-13 1.99756466167503e+00\t3.92193968439474e+00 5.69757722960217e+00 1.81209841891463e-01 1.93893855799501e+01\n1.01560856461994e-01\t1.39999999999999e+00 2.07360090856351e+00 -9.47117705939710e-14 2.00348209584885e+00\t3.92975642120161e+00 5.67484847655789e+00 1.84201120951481e-01 1.94518001906998e+01\n1.02652882690178e-01\t1.39999999999999e+00 2.06273675877702e+00 -8.75673630706194e-14 2.00928528125436e+00\t3.93742326460634e+00 5.65265276919526e+00 1.87222731780261e-01 1.95136616196767e+01\n1.03744913057449e-01\t1.39999999999999e+00 2.05184700614134e+00 -8.53204667042674e-14 2.01499573727912e+00\t3.94478442488305e+00 5.63095397921086e+00 1.90286607794568e-01 1.95733986864011e+01\n1.04836950361394e-01\t1.39999999999999e+00 2.04089560566707e+00 -8.59186789942889e-14 2.02062328915062e+00\t3.95179795475778e+00 5.60960054775238e+00 1.93392588456841e-01 1.96304602482221e+01\n1.05928997858608e-01\t1.40000000000000e+00 2.02986445723089e+00 -9.11946528886805e-14 2.02615378041638e+00\t3.95857779229572e+00 5.58860364349210e+00 1.96557135401527e-01 1.96863840118401e+01\n1.07021058961324e-01\t1.39999999999999e+00 2.01880613778244e+00 -9.32524504339132e-14 2.03159595983769e+00\t3.96511070083960e+00 5.56800577724938e+00 1.99772244662120e-01 1.97409252419469e+01\n1.08113136621244e-01\t1.40000000000000e+00 2.00771592109124e+00 -9.05705759093762e-14 2.03694739579659e+00\t3.97146277118337e+00 5.54787406161377e+00 2.03024086881321e-01 1.97945507713311e+01\n1.09205232171094e-01\t1.40000000000000e+00 1.99658729901778e+00 -9.32670521615471e-14 2.04222304433612e+00\t3.97761870469011e+00 5.52826012220895e+00 2.06335102674680e-01 1.98471886176577e+01\n1.10297347286810e-01\t1.40000000000000e+00 1.98544695456307e+00 -9.31481319087172e-14 2.04740782031778e+00\t3.98371788117021e+00 5.50917613323965e+00 2.09697582156646e-01 1.98999707950006e+01\n1.11389483372699e-01\t1.39999999999999e+00 1.97429875930195e+00 -9.28022257220457e-14 2.05251313845006e+00\t3.98975395765722e+00 5.49058009518171e+00 2.13116229953732e-01 1.99527207975152e+01\n1.12481641493235e-01\t1.39999999999999e+00 1.96313593894860e+00 -9.14792934360009e-14 2.05754725521838e+00\t3.99568366267697e+00 5.47245741489369e+00 2.16566179882599e-01 2.00051582513429e+01\n1.13573822337395e-01\t1.40000000000000e+00 1.95191990819423e+00 -8.76212920224841e-14 2.06251953169440e+00\t4.00142981652174e+00 5.45479945416955e+00 2.20021340252950e-01 2.00566603988125e+01\n1.14666025906356e-01\t1.39999999999999e+00 1.94067880509400e+00 -8.64709510107145e-14 2.06742607644170e+00\t4.00706143308924e+00 5.43767336247272e+00 2.23475000389359e-01 2.01078535015146e+01\n1.15758251202232e-01\t1.40000000000000e+00 1.92942524736330e+00 -9.47498726487095e-14 2.07224864047633e+00\t4.01280311501361e+00 5.42104357635243e+00 2.26903701937176e-01 2.01609477255211e+01\n1.16850496965182e-01\t1.39999999999998e+00 1.91813650192528e+00 -1.04360675325357e-13 2.07699750843351e+00\t4.01864046287459e+00 5.40487673697969e+00 2.30297063380139e-01 2.02157044052709e+01\n1.17942761821852e-01\t1.39999999999999e+00 1.90681201161359e+00 -1.10062062458213e-13 2.08169685218841e+00\t4.02445441549410e+00 5.38915698463400e+00 2.33659579951023e-01 2.02710060476138e+01\n1.19035043785844e-01\t1.39999999999999e+00 1.89542810837275e+00 -1.14630182140119e-13 2.08635088950400e+00\t4.03027020062537e+00 5.37383424201219e+00 2.36985049052178e-01 2.03272676960174e+01\n1.20127340174141e-01\t1.39999999999999e+00 1.88398181705159e+00 -1.20468186418471e-13 2.09096969402654e+00\t4.03599826458375e+00 5.35889524142141e+00 2.40285731664791e-01 2.03835611666215e+01\n1.21219647934225e-01\t1.39999999999999e+00 1.87248586195038e+00 -1.22655357563255e-13 2.09555535359628e+00\t4.04161847452790e+00 5.34442155113845e+00 2.43587020050162e-01 2.04397042041126e+01\n1.22311961494420e-01\t1.40000000000000e+00 1.86095574099232e+00 -1.20249447093318e-13 2.10010592656840e+00\t4.04718558000909e+00 5.33042223090680e+00 2.46902313468997e-01 2.04962983211407e+01\n1.23404277534410e-01\t1.39999999999998e+00 1.84939760939378e+00 -1.25247959503730e-13 2.10462119085933e+00\t4.05276098157414e+00 5.31691775994973e+00 2.50240545764909e-01 2.05536373772267e+01\n1.24496592270720e-01\t1.39999999999999e+00 1.83783925590130e+00 -1.32665572659140e-13 2.10909512964488e+00\t4.05837087628103e+00 5.30391671801282e+00 2.53586819402414e-01 2.06120075511993e+01\n1.25588901795314e-01\t1.39999999999999e+00 1.82623254611670e+00 -1.28383354517716e-13 2.11354176779507e+00\t4.06395596635095e+00 5.29125208578850e+00 2.56885517281312e-01 2.06709822045052e+01\n1.26681201657765e-01\t1.39999999999999e+00 1.81457440072592e+00 -1.30085200919387e-13 2.11798024897059e+00\t4.06938046388616e+00 5.27890787431916e+00 2.60148579337719e-01 2.07293083838867e+01\n1.27773487380486e-01\t1.39999999999998e+00 1.80291526801718e+00 -1.36129621180631e-13 2.12239836067876e+00\t4.07475804391487e+00 5.26704070052609e+00 2.63438947441450e-01 2.07882627710069e+01\n1.28865755085910e-01\t1.40000000000000e+00 1.79129412794866e+00 -1.43759690189588e-13 2.12679498299095e+00\t4.08013148221335e+00 5.25565656511653e+00 2.66758624653716e-01 2.08481517292411e+01\n1.29958000899039e-01\t1.39999999999999e+00 1.77967297776052e+00 -1.50635205250673e-13 2.13118960386552e+00\t4.08539543713525e+00 5.24460504587294e+00 2.70102874989292e-01 2.09078611687624e+01\n1.31050220430666e-01\t1.39999999999999e+00 1.76797516786399e+00 -1.41704171029727e-13 2.13557956842155e+00\t4.09059067247577e+00 5.23374685585761e+00 2.73430467471362e-01 2.09678023866288e+01\n1.32142410244611e-01\t1.39999999999999e+00 1.75620920449202e+00 -1.31275191568412e-13 2.13995906502824e+00\t4.09588642391178e+00 5.22308836188129e+00 2.76740358939742e-01 2.10295038405508e+01\n1.33234568639281e-01\t1.39999999999999e+00 1.74442690624446e+00 -1.34679629504076e-13 2.14432881749732e+00\t4.10125472438682e+00 5.21274381660654e+00 2.80073598056554e-01 2.10927073133884e+01\n1.34326695586857e-01\t1.39999999999999e+00 1.73265280748694e+00 -1.23907967986333e-13 2.14868130179614e+00\t4.10670404580677e+00 5.20270577029689e+00 2.83400552733498e-01 2.11575855271863e+01\n1.35418792398583e-01\t1.39999999999998e+00 1.72081170047981e+00 -1.19393648291082e-13 2.15301602050452e+00\t4.11227431184022e+00 5.19283650842749e+00 2.86644706286090e-01 2.12242781957851e+01\n1.36510861662084e-01\t1.39999999999999e+00 1.70891814235414e+00 -1.06291585433693e-13 2.15734377893181e+00\t4.11791930611375e+00 5.18309974447353e+00 2.89808634698694e-01 2.12923285172783e+01\n1.37602906837589e-01\t1.39999999999999e+00 1.69702980753768e+00 -1.01275297318541e-13 2.16169365165805e+00\t4.12345801163267e+00 5.17351786668960e+00 2.92871615947406e-01 2.13599371431563e+01\n1.38694931886007e-01\t1.39999999999999e+00 1.68517644801778e+00 -1.08217981759688e-13 2.16605931714485e+00\t4.12894598915741e+00 5.16398364610426e+00 2.95821474908186e-01 2.14278965801506e+01\n1.39786940832435e-01\t1.39999999999999e+00 1.67331373739021e+00 -1.07161233666506e-13 2.17045474726938e+00\t4.13436122707578e+00 5.15420419975425e+00 2.98569016233308e-01 2.14963413990575e+01\n1.40878936488535e-01\t1.39999999999999e+00 1.66150480097583e+00 -9.80014868108553e-14 2.17488014782197e+00\t4.13968414581331e+00 5.14423213694899e+00 3.01085620373124e-01 2.15650986716698e+01\n1.41970921422407e-01\t1.39999999999999e+00 1.64981082922074e+00 -9.33837871705456e-14 2.17931946107181e+00\t4.14499601025118e+00 5.13406292497200e+00 3.03350141576299e-01 2.16344634309887e+01\n1.43062898684278e-01\t1.39999999999999e+00 1.63821078284007e+00 -9.76413514627113e-14 2.18376771321467e+00\t4.15039242953482e+00 5.12350851270973e+00 3.05378482255847e-01 2.17052220341906e+01\n1.44154869459427e-01\t1.39999999999998e+00 1.62666641941892e+00 -1.02829886944913e-13 2.18822464604781e+00\t4.15591207018949e+00 5.11260058486951e+00 3.07227122231451e-01 2.17776295242951e+01\n1.45246833471082e-01\t1.39999999999999e+00 1.61521899594005e+00 -9.34737332395916e-14 2.19270124594617e+00\t4.16152628819541e+00 5.10130054387543e+00 3.08915020452513e-01 2.18513189720224e+01\n1.46338789792257e-01\t1.40000000000000e+00 1.60387844236282e+00 -8.56185769512698e-14 2.19721660235831e+00\t4.16718078658800e+00 5.08950105518011e+00 3.10448341162878e-01 2.19260535864444e+01\n1.47430732267331e-01\t1.39999999999998e+00 1.59273829202820e+00 -8.46353103686928e-14 2.20177579786987e+00\t4.17285841922633e+00 5.07731053394223e+00 3.11898602716119e-01 2.20017820272544e+01\n1.48522657026877e-01\t1.39999999999999e+00 1.58181877721498e+00 -8.44064830982457e-14 2.20638352146404e+00\t4.17856555507132e+00 5.06465459782214e+00 3.13324361839887e-01 2.20787270332900e+01\n1.49614564109156e-01\t1.39999999999998e+00 1.57112201695724e+00 -8.13289719692223e-14 2.21106447267421e+00\t4.18420700311755e+00 5.05114616664104e+00 3.14713099518929e-01 2.21558869928933e+01\n1.50706455108975e-01\t1.39999999999999e+00 1.56052580695628e+00 -7.78951156362740e-14 2.21581929867054e+00\t4.18984400614592e+00 5.03646422321996e+00 3.16012839992402e-01 2.22341548370168e+01\n1.51798332624505e-01\t1.39999999999999e+00 1.55008933137704e+00 -9.51148370274841e-14 2.22064284509141e+00\t4.19547974609541e+00 5.02087585981312e+00 3.17337536557949e-01 2.23136236801777e+01\n1.52890199928653e-01\t1.39999999999998e+00 1.53987709756306e+00 -1.05158325147477e-13 2.22553143569186e+00\t4.20116653507296e+00 5.00438197316178e+00 3.18785994452365e-01 2.23947224191893e+01\n1.53982060660259e-01\t1.39999999999998e+00 1.52992753273748e+00 -1.14057727068372e-13 2.23047305961096e+00\t4.20690075107559e+00 4.98707423606340e+00 3.20367674850314e-01 2.24771692866751e+01\n1.55073918322068e-01\t1.39999999999999e+00 1.52025175445308e+00 -1.19213727494054e-13 2.23543903724803e+00\t4.21273427770990e+00 4.96925579401590e+00 3.22064584319226e-01 2.25612035034528e+01\n1.56165775687605e-01\t1.39999999999999e+00 1.51079926655181e+00 -1.36068527494893e-13 2.24042164968372e+00\t4.21872207489756e+00 4.95097860550423e+00 3.23821675209202e-01 2.26472939817203e+01\n1.57257635857387e-01\t1.39999999999998e+00 1.50152457569565e+00 -1.52198507193434e-13 2.24544292712367e+00\t4.22486846664827e+00 4.93206815745075e+00 3.25608187226607e-01 2.27351107947971e+01\n1.58349501159884e-01\t1.39999999999999e+00 1.49238382804867e+00 -1.72659675989424e-13 2.25050487688658e+00\t4.23111561087754e+00 4.91256342018751e+00 3.27424509701647e-01 2.28239682470207e+01\n1.59441372372521e-01\t1.39999999999999e+00 1.48335959373955e+00 -1.99497314638018e-13 2.25559937920954e+00\t4.23743910118391e+00 4.89252737747591e+00 3.29310110927002e-01 2.29139409538862e+01\n1.60533248780972e-01\t1.39999999999999e+00 1.47447007081735e+00 -2.20618821131445e-13 2.26072644180967e+00\t4.24379398205225e+00 4.87205879426711e+00 3.31264656395776e-01 2.30046119035062e+01\n1.61625128479877e-01\t1.39999999999999e+00 1.46572780543471e+00 -2.40525640138185e-13 2.26590064033557e+00\t4.25013152938043e+00 4.85122977201563e+00 3.33273320996399e-01 2.30955627065839e+01\n1.62717008923964e-01\t1.39999999999999e+00 1.45713835420748e+00 -2.66007467536990e-13 2.27113040282152e+00\t4.25645817634508e+00 4.83005962778763e+00 3.35313852574494e-01 2.31868622265335e+01\n1.63808887570854e-01\t1.39999999999998e+00 1.44866514422296e+00 -2.74020742337162e-13 2.27641370417236e+00\t4.26278528065882e+00 4.80849254681259e+00 3.37393539055426e-01 2.32786103278382e+01\n1.64900762757469e-01\t1.40000000000000e+00 1.44024370407613e+00 -2.76362485450530e-13 2.28174346775678e+00\t4.26907163294592e+00 4.78657547486132e+00 3.39519264471280e-01 2.33705756855968e+01\n1.65992634542038e-01\t1.39999999999998e+00 1.43192986115545e+00 -2.99294169121793e-13 2.28709627135078e+00\t4.27543792774976e+00 4.76456513325533e+00 3.41756233212734e-01 2.34637639177119e+01\n1.67084505136532e-01\t1.39999999999999e+00 1.42374469212608e+00 -3.26065698770937e-13 2.29246145211616e+00\t4.28192857722067e+00 4.74260027058110e+00 3.44076132282002e-01 2.35580540452004e+01\n1.68176379941894e-01\t1.39999999999999e+00 1.41567205354604e+00 -3.45998110207073e-13 2.29782563529021e+00\t4.28859405053911e+00 4.72073748559948e+00 3.46466763916271e-01 2.36536076332569e+01\n1.69268268832591e-01\t1.39999999999999e+00 1.40770600635008e+00 -3.53329431307713e-13 2.30320891032874e+00\t4.29540490422966e+00 4.69890559612922e+00 3.48891868492982e-01 2.37498450895448e+01\n1.70360183985993e-01\t1.39999999999998e+00 1.39983418659642e+00 -3.57925808700016e-13 2.30862338832461e+00\t4.30229667074959e+00 4.67698880045664e+00 3.51223933680016e-01 2.38464884032127e+01\n1.71452144783598e-01\t1.39999999999999e+00 1.39205465939361e+00 -3.73182070596903e-13 2.31408030986686e+00\t4.30921505441712e+00 4.65498469203835e+00 3.53661689906255e-01 2.39433817788300e+01\n1.72544176905568e-01\t1.39999999999998e+00 1.38437441367800e+00 -3.85732714041198e-13 2.31956120159684e+00\t4.31620736226103e+00 4.63308975209023e+00 3.56333663579485e-01 2.40407177179639e+01\n1.73636312707653e-01\t1.39999999999999e+00 1.37678301397662e+00 -3.99233501503373e-13 2.32505537125979e+00\t4.32333105839389e+00 4.61126163910123e+00 3.59193736431582e-01 2.41392941527686e+01\n1.74728591489993e-01\t1.39999999999998e+00 1.36928061932095e+00 -4.16131125118573e-13 2.33056578584015e+00\t4.33061086126478e+00 4.58943412431792e+00 3.62182533972520e-01 2.42391466326283e+01\n1.75821059440837e-01\t1.39999999999999e+00 1.36186163554156e+00 -4.37653835214706e-13 2.33609088892899e+00\t4.33810954852860e+00 4.56755885297598e+00 3.65308492289549e-01 2.43411306303240e+01\n1.76913769425920e-01\t1.39999999999999e+00 1.35451663446468e+00 -4.59681544413168e-13 2.34164821509582e+00\t4.34577574219583e+00 4.54559381975559e+00 3.68452259571184e-01 2.44447425178562e+01\n1.78006773231766e-01\t1.39999999999998e+00 1.34721100044083e+00 -4.74604223465646e-13 2.34725495942234e+00\t4.35356418114228e+00 4.52333662976811e+00 3.71544512229611e-01 2.45497233911369e+01\n1.79100104488915e-01\t1.39999999999998e+00 1.33997375487722e+00 -4.77066312613884e-13 2.35290338353497e+00\t4.36147779750870e+00 4.50088090205939e+00 3.74751947040014e-01 2.46559930472261e+01\n1.80193830159202e-01\t1.39999999999998e+00 1.33282985424060e+00 -4.73806494831454e-13 2.35858259200268e+00\t4.36952497270059e+00 4.47833227734568e+00 3.78215127282470e-01 2.47635534599077e+01\n1.81288022500281e-01\t1.39999999999999e+00 1.32576195533496e+00 -4.63066916991995e-13 2.36430579573288e+00\t4.37759402028291e+00 4.45566064524680e+00 3.81885199418239e-01 2.48711132768187e+01\n1.82382759467649e-01\t1.39999999999998e+00 1.31875508394062e+00 -4.47283645930319e-13 2.37008156615374e+00\t4.38563181345625e+00 4.43276516160964e+00 3.85713536853572e-01 2.49779361135053e+01\n1.83478125942249e-01\t1.39999999999998e+00 1.31181882863102e+00 -4.42366331448894e-13 2.37588995415224e+00\t4.39371527662958e+00 4.40967440674574e+00 3.89627096553420e-01 2.50846586887586e+01\n1.84574216087251e-01\t1.39999999999999e+00 1.30496408480208e+00 -4.41042919125679e-13 2.38171437433593e+00\t4.40189675533818e+00 4.38659027881192e+00 3.93541413475108e-01 2.51921332963928e+01\n1.85671135903129e-01\t1.39999999999998e+00 1.29817786136131e+00 -4.50524441344316e-13 2.38754576477204e+00\t4.41020557052105e+00 4.36353633869302e+00 3.97409993569105e-01 2.53009872778384e+01\n1.86769003694135e-01\t1.39999999999998e+00 1.29147578555568e+00 -4.65195272836413e-13 2.39337712990827e+00\t4.41865876122291e+00 4.34056827071784e+00 4.01339240857855e-01 2.54114592241680e+01\n1.87867949499103e-01\t1.39999999999998e+00 1.28485780557322e+00 -4.72308914992512e-13 2.39920963478301e+00\t4.42724643375276e+00 4.31769428680506e+00 4.05458362938867e-01 2.55230138589242e+01\n1.88968114001685e-01\t1.39999999999998e+00 1.27833392067953e+00 -4.73489655970106e-13 2.40503984272619e+00\t4.43599910173813e+00 4.29491764768858e+00 4.09850568379048e-01 2.56353472363783e+01\n1.90069647051324e-01\t1.39999999999998e+00 1.27191301954955e+00 -4.69933569277279e-13 2.41087315386067e+00\t4.44487148258236e+00 4.27221131972304e+00 4.14517806302791e-01 2.57479662800771e+01\n1.91172706123969e-01\t1.39999999999998e+00 1.26557873424611e+00 -4.58247257833263e-13 2.41671410850072e+00\t4.45383359455973e+00 4.24948403003419e+00 4.19355262062417e-01 2.58608953863649e+01\n1.92277455593433e-01\t1.39999999999999e+00 1.25930602884222e+00 -4.44721988475443e-13 2.42255962296932e+00\t4.46290534722143e+00 4.22669147140691e+00 4.24235718018126e-01 2.59743444661937e+01\n1.93384062733848e-01\t1.39999999999999e+00 1.25307779556491e+00 -4.34561794005387e-13 2.42841588291953e+00\t4.47207332583424e+00 4.20375433619022e+00 4.29091385432125e-01 2.60881831563345e+01\n1.94492695210589e-01\t1.39999999999998e+00 1.24685870425868e+00 -4.31446553387787e-13 2.43430171644449e+00\t4.48127050494868e+00 4.18057984052878e+00 4.33915093833165e-01 2.62013552596105e+01\n1.95603518579959e-01\t1.39999999999998e+00 1.24063776832295e+00 -4.37197291207562e-13 2.44021305458413e+00\t4.49048087253864e+00 4.15720826089168e+00 4.38804922073958e-01 2.63134301149565e+01\n1.96716694386534e-01\t1.39999999999998e+00 1.23445488804118e+00 -4.44494099785134e-13 2.44615670005367e+00\t4.49960524991290e+00 4.13376561312996e+00 4.43840817098599e-01 2.64232308067250e+01\n1.97832378771677e-01\t1.39999999999998e+00 1.22834223260900e+00 -4.49568916611297e-13 2.45210319418327e+00\t4.50879309693297e+00 4.11036390314475e+00 4.49085295694777e-01 2.65324459143988e+01\n1.98950721801476e-01\t1.39999999999998e+00 1.22230951863658e+00 -4.62100365733545e-13 2.45804117370190e+00\t4.51814251171261e+00 4.08699992424758e+00 4.54548257081129e-01 2.66419465769267e+01\n2.00071869679950e-01\t1.39999999999999e+00 1.21636654313442e+00 -4.69329747000634e-13 2.46397082673879e+00\t4.52761169898922e+00 4.06380571604454e+00 4.60176548350918e-01 2.67516784788824e+01\n2.01195968623682e-01\t1.39999999999998e+00 1.21049258837204e+00 -4.74381371583250e-13 2.46989326794361e+00\t4.53719958174414e+00 4.04072969551545e+00 4.65908462436116e-01 2.68623380056407e+01\n2.02323157255617e-01\t1.39999999999999e+00 1.20466810313824e+00 -4.79397545062660e-13 2.47581485372215e+00\t4.54691955976953e+00 4.01766467441457e+00 4.71685230346103e-01 2.69734182133441e+01\n2.03453561986681e-01\t1.39999999999998e+00 1.19886143080382e+00 -4.78176774716915e-13 2.48174063076766e+00\t4.55674699279379e+00 3.99450016696377e+00 4.77459201641407e-01 2.70846536182250e+01\n2.04587297618819e-01\t1.39999999999998e+00 1.19305478714169e+00 -4.74201336150480e-13 2.48766886916729e+00\t4.56667393669658e+00 3.97124222947279e+00 4.83276187424161e-01 2.71959419122843e+01\n2.05724469094623e-01\t1.39999999999999e+00 1.18725395053412e+00 -4.80147750017070e-13 2.49358307441600e+00\t4.57672555474636e+00 3.94800152938046e+00 4.89151293963920e-01 2.73076598369196e+01\n2.06865171355194e-01\t1.39999999999998e+00 1.18147110926247e+00 -4.88845041960132e-13 2.49949752270732e+00\t4.58679530326437e+00 3.92483266797215e+00 4.95144984937086e-01 2.74185673869248e+01\n2.08009487855709e-01\t1.39999999999998e+00 1.17573308073323e+00 -4.90281829032158e-13 2.50542024655706e+00\t4.59679089563750e+00 3.90172293959711e+00 5.01276475923555e-01 2.75279489753410e+01\n2.09157489666880e-01\t1.39999999999999e+00 1.17002379692328e+00 -4.90132129082608e-13 2.51136151473309e+00\t4.60664275159256e+00 3.87859867905149e+00 5.07521763679956e-01 2.76350136883251e+01\n2.10309236084767e-01\t1.39999999999999e+00 1.16435502564807e+00 -4.95777803244865e-13 2.51731208610245e+00\t4.61642597750097e+00 3.85545027740543e+00 5.13863315942452e-01 2.77406445113462e+01\n2.11464776384187e-01\t1.39999999999999e+00 1.15872562439776e+00 -5.03060432814101e-13 2.52326966058995e+00\t4.62617881471690e+00 3.83222090307201e+00 5.20334682306717e-01 2.78453693220486e+01\n2.12624153212636e-01\t1.39999999999998e+00 1.15314016154663e+00 -5.03819163801821e-13 2.52921788998454e+00\t4.63593478348732e+00 3.80896336797155e+00 5.26872623840457e-01 2.79497624370453e+01\n2.13787404257123e-01\t1.39999999999999e+00 1.14759674597175e+00 -5.01566893885064e-13 2.53514265835314e+00\t4.64573165364457e+00 3.78573093886675e+00 5.33486135850589e-01 2.80541644650924e+01\n2.14954565157613e-01\t1.39999999999998e+00 1.14208083797049e+00 -5.02376852719978e-13 2.54104761364437e+00\t4.65552939446638e+00 3.76253599885567e+00 5.40171796792893e-01 2.81578333290696e+01\n2.16125671481103e-01\t1.39999999999998e+00 1.13659534843325e+00 -4.91407226022948e-13 2.54694261307976e+00\t4.66529223252862e+00 3.73935911300310e+00 5.46947608778001e-01 2.82602571548177e+01\n2.17300761255642e-01\t1.39999999999998e+00 1.13113898622877e+00 -4.76988847511246e-13 2.55283667567917e+00\t4.67498897309829e+00 3.71609130205660e+00 5.53748215470906e-01 2.83612050579530e+01\n2.18479875489244e-01\t1.39999999999999e+00 1.12569335555855e+00 -4.71868219376696e-13 2.55872635218155e+00\t4.68459913172087e+00 3.69269668985968e+00 5.60546841957350e-01 2.84605620227624e+01\n2.19663061457399e-01\t1.39999999999998e+00 1.12025476608368e+00 -4.63393471232475e-13 2.56459513802102e+00\t4.69408005236535e+00 3.66934214230600e+00 5.67376819034430e-01 2.85576436578876e+01\n2.20850374729862e-01\t1.39999999999998e+00 1.11483379715139e+00 -4.47025977023665e-13 2.57044718376671e+00\t4.70332975665920e+00 3.64605407002519e+00 5.74220864174145e-01 2.86514062394951e+01\n2.22041878199102e-01\t1.39999999999998e+00 1.10944931024911e+00 -4.26275825851628e-13 2.57628293136204e+00\t4.71238221436173e+00 3.62285870990474e+00 5.80992297601240e-01 2.87421070141207e+01\n2.23237642633269e-01\t1.39999999999999e+00 1.10408380472580e+00 -4.07023680820197e-13 2.58204903929403e+00\t4.72128998908415e+00 3.59983946674094e+00 5.87942117893825e-01 2.88299030338156e+01\n2.24437748034689e-01\t1.39999999999999e+00 1.09873059462424e+00 -3.89165635376376e-13 2.58773476170541e+00\t4.73012630131087e+00 3.57704732106088e+00 5.95119438870795e-01 2.89158437041918e+01\n2.25642382982872e-01\t1.39999999999999e+00 1.09338888640945e+00 -3.76927420899730e-13 2.59336803196230e+00\t4.73883298887586e+00 3.55438268489087e+00 6.02457681287366e-01 2.89995412921859e+01\n2.26851786682807e-01\t1.39999999999998e+00 1.08808450328943e+00 -3.63819758802818e-13 2.59894455416572e+00\t4.74745261060151e+00 3.53186729068245e+00 6.09877642211228e-01 2.90813565761994e+01\n2.28066091965857e-01\t1.39999999999999e+00 1.08280747631060e+00 -3.48019238644342e-13 2.60448059032904e+00\t4.75591735023321e+00 3.50938040652908e+00 6.17357467088109e-01 2.91603983420209e+01\n2.29285363541428e-01\t1.39999999999998e+00 1.07754833122503e+00 -3.33472887238562e-13 2.60999857791759e+00\t4.76410258932996e+00 3.48683467198012e+00 6.24852196880702e-01 2.92354193754015e+01\n2.30509651844400e-01\t1.39999999999999e+00 1.07231342521566e+00 -3.30843394083373e-13 2.61551570004517e+00\t4.77191935064153e+00 3.46419420312492e+00 6.32281698248069e-01 2.93055852914544e+01\n2.31738993381130e-01\t1.39999999999999e+00 1.06712632602401e+00 -3.14697644805576e-13 2.62099085041975e+00\t4.77937930670232e+00 3.44160398647194e+00 6.39816473494707e-01 2.93711155613331e+01\n2.32973406682462e-01\t1.39999999999998e+00 1.06197189487357e+00 -3.12519830803231e-13 2.62638568425707e+00\t4.78658375106557e+00 3.41920187364931e+00 6.47572317401275e-01 2.94327858424590e+01\n2.34212924725592e-01\t1.39999999999998e+00 1.05681373092737e+00 -3.08681016547027e-13 2.63171040551333e+00\t4.79360291069029e+00 3.39694362743598e+00 6.55456149326204e-01 2.94913507182228e+01\n2.35457600916080e-01\t1.39999999999998e+00 1.05165502752851e+00 -3.10791585939604e-13 2.63695667413347e+00\t4.80042119902946e+00 3.37482744122417e+00 6.63488984243682e-01 2.95471387420780e+01\n2.36707462102902e-01\t1.39999999999998e+00 1.04651159766616e+00 -3.01253945942904e-13 2.64211432007897e+00\t4.80703743942386e+00 3.35289469587040e+00 6.71733182797200e-01 2.96004753295696e+01\n2.37962500223532e-01\t1.39999999999998e+00 1.04137717080189e+00 -2.80986002854745e-13 2.64720033027860e+00\t4.81344758983508e+00 3.33106913320460e+00 6.80104215239051e-01 2.96509762656240e+01\n2.39222677470022e-01\t1.39999999999998e+00 1.03625765425489e+00 -2.64656668018545e-13 2.65220670301794e+00\t4.81964193379483e+00 3.30934867590634e+00 6.88504312282810e-01 2.96978258555428e+01\n2.40487898392335e-01\t1.39999999999998e+00 1.03113648926367e+00 -2.52107396620787e-13 2.65714993696758e+00\t4.82554383179158e+00 3.28769216127414e+00 6.96898825395096e-01 2.97400913077007e+01\n2.41758143629112e-01\t1.39999999999998e+00 1.02600669043170e+00 -2.46755576853612e-13 2.66203467150007e+00\t4.83111154687363e+00 3.26605903730945e+00 7.05403674966161e-01 2.97777017541029e+01\n2.43033451255392e-01\t1.39999999999998e+00 1.02087011763341e+00 -2.46163608629161e-13 2.66685672793992e+00\t4.83641799320507e+00 3.24444352252219e+00 7.14042483988744e-01 2.98118305968450e+01\n2.44313983005739e-01\t1.39999999999998e+00 1.01574535617622e+00 -2.51225489528316e-13 2.67163263036075e+00\t4.84153858003535e+00 3.22280583538410e+00 7.22834076461408e-01 2.98432803262210e+01\n2.45600001761595e-01\t1.39999999999998e+00 1.01064217970010e+00 -2.53262720704965e-13 2.67635023274457e+00\t4.84648591648492e+00 3.20121182668321e+00 7.31845939106785e-01 2.98720370885329e+01\n2.46891785447333e-01\t1.39999999999998e+00 1.00556951170150e+00 -2.46516135029931e-13 2.68100284106937e+00\t4.85122618706927e+00 3.17971117787761e+00 7.41112761380011e-01 2.98981503487278e+01\n2.48189615147918e-01\t1.39999999999998e+00 1.00048844628286e+00 -2.44792382454443e-13 2.68560100399397e+00\t4.85563296800586e+00 3.15829248535109e+00 7.50649969255668e-01 2.99204932128233e+01\n2.49493762913897e-01\t1.39999999999999e+00 9.95398725971232e-01 -2.41450112914598e-13 2.69012489860156e+00\t4.85981737428235e+00 3.13698058853778e+00 7.60347364160477e-01 2.99391517664654e+01\n2.50804470599720e-01\t1.39999999999998e+00 9.90314507943466e-01 -2.33729854235643e-13 2.69457033970718e+00\t4.86372621567360e+00 3.11581458035277e+00 7.70205518730080e-01 2.99535861957199e+01\n2.52121940214170e-01\t1.39999999999998e+00 9.85213620716955e-01 -2.29731950646149e-13 2.69897519180673e+00\t4.86715030657549e+00 3.09467692263235e+00 7.80225410461202e-01 2.99619974491445e+01\n2.53446389399841e-01\t1.39999999999999e+00 9.80103329108154e-01 -2.32439403270613e-13 2.70337084209649e+00\t4.87008750403885e+00 3.07349579906116e+00 7.90359259067490e-01 2.99645891483518e+01\n2.54777963985013e-01\t1.39999999999998e+00 9.74974823996187e-01 -2.29245956000418e-13 2.70772471414848e+00\t4.87268266995840e+00 3.05235506015868e+00 8.00672160105255e-01 2.99629195973748e+01\n2.56116756064054e-01\t1.39999999999998e+00 9.69790154515563e-01 -2.17746665604699e-13 2.71202887962358e+00\t4.87500965171239e+00 3.03128814593557e+00 8.11275650753840e-01 2.99580713424563e+01\n2.57462749870346e-01\t1.39999999999998e+00 9.64560319718652e-01 -2.00995711256621e-13 2.71630144162041e+00\t4.87697562304683e+00 3.01027725712131e+00 8.22226429982089e-01 2.99496825773564e+01\n2.58815721775876e-01\t1.39999999999998e+00 9.59317803017237e-01 -1.87893677058167e-13 2.72050831421051e+00\t4.87872540547957e+00 2.98941655951432e+00 8.33421119847953e-01 2.99385519668941e+01\n2.60175452439701e-01\t1.39999999999999e+00 9.54068408882156e-01 -1.85766095005008e-13 2.72462229526087e+00\t4.88035237225720e+00 2.96870072600873e+00 8.44896877740239e-01 2.99251252055998e+01\n2.61542007232891e-01\t1.39999999999998e+00 9.48801343518497e-01 -1.94929760970088e-13 2.72866452064269e+00\t4.88187447273233e+00 2.94809150005208e+00 8.56609480803278e-01 2.99099113811300e+01\n2.62915513264442e-01\t1.39999999999998e+00 9.43505280101055e-01 -2.09254209820710e-13 2.73263011813185e+00\t4.88325476015834e+00 2.92761960022467e+00 8.68548431019766e-01 2.98924063901522e+01\n2.64296043042365e-01\t1.39999999999998e+00 9.38203017005885e-01 -2.21235156615799e-13 2.73650334153352e+00\t4.88456654786637e+00 2.90732437053360e+00 8.80758501567412e-01 2.98730124940591e+01\n2.65683692362729e-01\t1.39999999999998e+00 9.32878103497003e-01 -2.29034359914937e-13 2.74031301544075e+00\t4.88571833740318e+00 2.88715053768891e+00 8.93340289444005e-01 2.98513295761028e+01\n2.67079492902547e-01\t1.39999999999998e+00 9.27528350429382e-01 -2.36759032069957e-13 2.74407595090794e+00\t4.88665369907201e+00 2.86704392374060e+00 9.06320903163877e-01 2.98271980955097e+01\n2.68485202530699e-01\t1.39999999999998e+00 9.22137741355095e-01 -2.37657876593304e-13 2.74777706936923e+00\t4.88745833525261e+00 2.84703289678460e+00 9.19461619838985e-01 2.98010540711023e+01\n2.69903211794964e-01\t1.39999999999998e+00 9.16705670936020e-01 -2.30784507370015e-13 2.75142666993341e+00\t4.88808599399611e+00 2.82710937088259e+00 9.32575080339949e-01 2.97723366487249e+01\n2.71335838468342e-01\t1.39999999999998e+00 9.11208765115285e-01 -2.14791624854140e-13 2.75504817000441e+00\t4.88856857321097e+00 2.80720429634606e+00 9.45509982743077e-01 2.97414465230356e+01\n2.72786998256924e-01\t1.39999999999998e+00 9.05615923661692e-01 -2.00117221742208e-13 2.75867127353728e+00\t4.88898433980213e+00 2.78723727148392e+00 9.58105182129775e-01 2.97092896470745e+01\n2.74264056689626e-01\t1.39999999999998e+00 8.99906500160764e-01 -1.84361022627472e-13 2.76234615308024e+00\t4.88931147732999e+00 2.76710807315536e+00 9.70005610927314e-01 2.96757828619146e+01\n2.75747782571136e-01\t1.39999999999998e+00 8.94186547476220e-01 -1.73386828412597e-13 2.76604090607621e+00\t4.88959127934048e+00 2.74714231985292e+00 9.80957000890854e-01 2.96416325704067e+01\n2.77219261169940e-01\t1.39999999999997e+00 8.88511340101164e-01 -1.71067446433648e-13 2.76974474442533e+00\t4.88979403243804e+00 2.72756227960521e+00 9.90854346345027e-01 2.96071963293200e+01\n2.78659692350165e-01\t1.39999999999997e+00 8.82957744650693e-01 -1.72050816904867e-13 2.77339667165973e+00\t4.89001466458506e+00 2.70866443912805e+00 9.99547505501820e-01 2.95734364134450e+01\n2.80078847555016e-01\t1.39999999999998e+00 8.77513809245619e-01 -1.68903797110171e-13 2.77704365936494e+00\t4.89026156570696e+00 2.69032311090232e+00 1.00697134162028e+00 2.95401387647574e+01\n2.81476538746033e-01\t1.39999999999998e+00 8.72150386383342e-01 -1.63315415742333e-13 2.78069009542291e+00\t4.89053019373885e+00 2.67254143404263e+00 1.01303560072566e+00 2.95073031075288e+01\n2.82843711021570e-01\t1.39999999999998e+00 8.66881726794152e-01 -1.61369674634879e-13 2.78429098921699e+00\t4.89094034220930e+00 2.65541365469380e+00 1.01758984531473e+00 2.94760209160952e+01\n2.84193471948875e-01\t1.39999999999998e+00 8.61640087472892e-01 -1.64585282426944e-13 2.78788333792018e+00\t4.89156862036061e+00 2.63864349471252e+00 1.02052873311970e+00 2.94466599462887e+01\n2.85519842746976e-01\t1.39999999999998e+00 8.56474692310267e-01 -1.59546926931463e-13 2.79147647725329e+00\t4.89233238454878e+00 2.62230854233299e+00 1.02180543333337e+00 2.94191566078094e+01\n2.86818505403307e-01\t1.39999999999998e+00 8.51438134636708e-01 -1.52226879539573e-13 2.79504784103215e+00\t4.89320897542818e+00 2.60651574860202e+00 1.02155629549694e+00 2.93933811030414e+01\n2.88102752678613e-01\t1.39999999999998e+00 8.46502710035021e-01 -1.51924886006811e-13 2.79861949117344e+00\t4.89421366917090e+00 2.59109432639579e+00 1.01990713551998e+00 2.93689436457942e+01\n2.89365713635941e-01\t1.39999999999998e+00 8.41705239980941e-01 -1.50883219678432e-13 2.80214641327337e+00\t4.89535294506037e+00 2.57616392498499e+00 1.01702650029214e+00 2.93458947085393e+01\n2.90597042957342e-01\t1.39999999999998e+00 8.37099229174806e-01 -1.53368103427489e-13 2.80561136777151e+00\t4.89650387667596e+00 2.56184416083300e+00 1.01311247891639e+00 2.93237466185567e+01\n2.91813291322434e-01\t1.39999999999998e+00 8.32623154364569e-01 -1.55278973775180e-13 2.80905642833357e+00\t4.89768273910102e+00 2.54783241321072e+00 1.00824970920166e+00 2.93020857749799e+01\n2.93007003478968e-01\t1.39999999999998e+00 8.28288878708771e-01 -1.58387078317843e-13 2.81246606275961e+00\t4.89887932106156e+00 2.53420195235045e+00 1.00255558670935e+00 2.92809683303570e+01\n2.94174154973176e-01\t1.39999999999998e+00 8.24108364868842e-01 -1.52445045686016e-13 2.81579668182861e+00\t4.90012293422881e+00 2.52100903906110e+00 9.96246639558547e-01 2.92607037692874e+01\n2.95325371543406e-01\t1.39999999999998e+00 8.20041120978630e-01 -1.40502157957263e-13 2.81905024960302e+00\t4.90138422813840e+00 2.50810696436930e+00 9.89466731225771e-01 2.92407649740646e+01\n2.96468119987664e-01\t1.39999999999998e+00 8.16046046376543e-01 -1.35782335782689e-13 2.82225230585820e+00\t4.90259788896930e+00 2.49541760847934e+00 9.82281144103691e-01 2.92207623494493e+01\n2.97616158118359e-01\t1.39999999999998e+00 8.12076567785425e-01 -1.29486985046999e-13 2.82544198126696e+00\t4.90372922834467e+00 2.48270841758808e+00 9.74665587100750e-01 2.92001654320857e+01\n2.98776107246171e-01\t1.39999999999997e+00 8.08098370300657e-01 -1.26762304819935e-13 2.82864706031708e+00\t4.90471441772421e+00 2.46979282712346e+00 9.66648766006296e-01 2.91784282747203e+01\n2.99949683361908e-01\t1.39999999999998e+00 8.04096397928924e-01 -1.25274870244159e-13 2.83187324286768e+00\t4.90552339081500e+00 2.45658475354784e+00 9.58314280921323e-01 2.91554051028647e+01\n3.01141248200584e-01\t1.39999999999998e+00 8.00048053897574e-01 -1.23978698239131e-13 2.83512103285562e+00\t4.90615833048016e+00 2.44309227232777e+00 9.49709781681724e-01 2.91310633605882e+01\n3.02354314363511e-01\t1.39999999999997e+00 7.95945504065095e-01 -1.10755039100810e-13 2.83839201373628e+00\t4.90659754694797e+00 2.42939077344229e+00 9.40881748241787e-01 2.91053438396042e+01\n3.03580428248641e-01\t1.39999999999998e+00 7.91830471103425e-01 -1.03906409703585e-13 2.84163810821779e+00\t4.90686299231484e+00 2.41561459564475e+00 9.31973754966407e-01 2.90787407649946e+01\n3.04794441319210e-01\t1.39999999999998e+00 7.87788710479397e-01 -1.07215420548790e-13 2.84480258364540e+00\t4.90692681026050e+00 2.40200495308906e+00 9.23182441987154e-01 2.90519045169077e+01\n3.06006469146398e-01\t1.39999999999998e+00 7.83799434273581e-01 -9.62275739335194e-14 2.84790843579169e+00\t4.90678445827081e+00 2.38847925318607e+00 9.14455755528913e-01 2.90246033216376e+01\n3.07226228146269e-01\t1.39999999999998e+00 7.79822158756121e-01 -9.58591167469816e-14 2.85098771967009e+00\t4.90643514426168e+00 2.37490611338432e+00 9.05757626382389e-01 2.89964615808833e+01\n3.08464483294702e-01\t1.39999999999998e+00 7.75806955799600e-01 -1.00444127288994e-13 2.85406337765824e+00\t4.90588188470911e+00 2.36118172888488e+00 8.97070274314790e-01 2.89672731134381e+01\n3.09708278079317e-01\t1.39999999999999e+00 7.71776988041483e-01 -1.02114065548360e-13 2.85710304402534e+00\t4.90510507180824e+00 2.34745701836743e+00 8.88557533761748e-01 2.89372382121373e+01\n3.10940024805410e-01\t1.39999999999998e+00 7.67789174025963e-01 -1.03287272808286e-13 2.86005523884260e+00\t4.90425931250612e+00 2.33397633018235e+00 8.80395717733586e-01 2.89076603871314e+01\n3.12161570436883e-01\t1.39999999999998e+00 7.63826709079621e-01 -9.94097618244325e-14 2.86292847038145e+00\t4.90332609005860e+00 2.32069528750817e+00 8.72592526252577e-01 2.88783922497263e+01\n3.13379761410732e-01\t1.39999999999997e+00 7.59861989817654e-01 -1.03330203893656e-13 2.86575820089208e+00\t4.90220279485403e+00 2.30749325616281e+00 8.65021498887158e-01 2.88488674714554e+01\n3.14590980185538e-01\t1.39999999999998e+00 7.55922108120523e-01 -1.17015855819231e-13 2.86853625531115e+00\t4.90094471582040e+00 2.29450324582657e+00 8.57648864992398e-01 2.88191151780900e+01\n3.15786138335383e-01\t1.39999999999997e+00 7.52043825572801e-01 -1.11707751675784e-13 2.87122482982763e+00\t4.89960725250281e+00 2.28191262750461e+00 8.50506996718829e-01 2.87894496378394e+01\n3.16972768790602e-01\t1.39999999999998e+00 7.48201848682005e-01 -1.10660787026706e-13 2.87382589834148e+00\t4.89817306602187e+00 2.26962571402857e+00 8.43855330881380e-01 2.87596203761655e+01\n3.18160437182428e-01\t1.39999999999998e+00 7.44326439543358e-01 -1.05535104172409e-13 2.87637576410201e+00\t4.89658548258288e+00 2.25741141940150e+00 8.37571407593868e-01 2.87289601328946e+01\n3.19356237977561e-01\t1.39999999999998e+00 7.40382147478825e-01 -8.59715726421089e-14 2.87887363874389e+00\t4.89489330905937e+00 2.24513210134408e+00 8.31705911826409e-01 2.86976731573540e+01\n3.20566177503363e-01\t1.39999999999998e+00 7.36349550711570e-01 -7.57186557686309e-14 2.88134174513250e+00\t4.89314303783836e+00 2.23269590856196e+00 8.26093421982936e-01 2.86658869478657e+01\n3.21791992953620e-01\t1.39999999999998e+00 7.32233351856604e-01 -7.46300389391802e-14 2.88379449255993e+00\t4.89135363171635e+00 2.22008909680349e+00 8.20632973175152e-01 2.86336592409782e+01\n3.23031647163947e-01\t1.39999999999997e+00 7.28039532528969e-01 -7.03729546841839e-14 2.88622985364668e+00\t4.88953847498115e+00 2.20743223099138e+00 8.15301167375958e-01 2.86010211084703e+01\n3.24288226983007e-01\t1.39999999999998e+00 7.23754173686119e-01 -6.32159587234168e-14 2.88864797965592e+00\t4.88770376592587e+00 2.19476701516013e+00 8.10105796113213e-01 2.85678388274819e+01\n3.25566459286630e-01\t1.39999999999997e+00 7.19357783145889e-01 -6.04985041304648e-14 2.89107062393946e+00\t4.88581546383151e+00 2.18200712722615e+00 8.04991317990065e-01 2.85338718743663e+01\n3.26864287052104e-01\t1.39999999999998e+00 7.14867928265989e-01 -5.20074454695807e-14 2.89350893565017e+00\t4.88373349107252e+00 2.16919961229426e+00 7.99878074038846e-01 2.84983326642975e+01\n3.28173061228863e-01\t1.39999999999998e+00 7.10300812325388e-01 -4.91582493325324e-14 2.89595471174847e+00\t4.88141659563747e+00 2.15636892787171e+00 7.94759236455696e-01 2.84612206169013e+01\n3.29490134500648e-01\t1.39999999999997e+00 7.05614411137670e-01 -5.69396196090596e-14 2.89836700449597e+00\t4.87912148874271e+00 2.14333009613389e+00 7.89709264406395e-01 2.84238873457644e+01\n3.30799927510114e-01\t1.39999999999997e+00 7.00877596852332e-01 -5.81892889088824e-14 2.90071758786782e+00\t4.87687276106920e+00 2.13036679643664e+00 7.84833656985060e-01 2.83868392864872e+01\n3.32102346219422e-01\t1.39999999999997e+00 6.96100410190108e-01 -7.52350792205830e-14 2.90301217754446e+00\t4.87468967803030e+00 2.11761062737823e+00 7.80190199520093e-01 2.83503320279643e+01\n3.33392100926241e-01\t1.39999999999997e+00 6.91323888122749e-01 -9.39439905473689e-14 2.90524441902217e+00\t4.87262303516352e+00 2.10509822656815e+00 7.75840303256193e-01 2.83147417607768e+01\n3.34667667278608e-01\t1.39999999999997e+00 6.86568468265607e-01 -1.07214994247125e-13 2.90741232434131e+00\t4.87064242109072e+00 2.09280380831062e+00 7.71707675705278e-01 2.82799476247520e+01\n3.35931110822421e-01\t1.39999999999997e+00 6.81790311414084e-01 -1.15699414782075e-13 2.90953730937377e+00\t4.86864476289637e+00 2.08065468386641e+00 7.67678548863055e-01 2.82452723935660e+01\n3.37188115145129e-01\t1.39999999999998e+00 6.76913136921666e-01 -1.14522334983490e-13 2.91164610241118e+00\t4.86662324495613e+00 2.06843175326115e+00 7.63683093885878e-01 2.82103588740690e+01\n3.38444708221923e-01\t1.39999999999997e+00 6.71943536290570e-01 -1.26737873077427e-13 2.91373752692351e+00\t4.86462730225637e+00 2.05622971499306e+00 7.59728403181131e-01 2.81755794528436e+01\n3.39699584853679e-01\t1.39999999999997e+00 6.66928032116622e-01 -1.33720117596656e-13 2.91580468260550e+00\t4.86266808790729e+00 2.04415550392687e+00 7.55859770277587e-01 2.81413091855279e+01\n3.40942064937134e-01\t1.39999999999997e+00 6.61923884920987e-01 -1.35397965722218e-13 2.91782410010796e+00\t4.86076538799432e+00 2.03230975332201e+00 7.52130150379101e-01 2.81079605697457e+01\n3.42177925001242e-01\t1.39999999999998e+00 6.56911917891193e-01 -1.44423771460474e-13 2.91980520099770e+00\t4.85891976800048e+00 2.02061205630491e+00 7.48493960699322e-01 2.80755984882983e+01\n3.43413464667360e-01\t1.39999999999998e+00 6.51826776710625e-01 -1.52250473008218e-13 2.92177017004338e+00\t4.85706775151099e+00 2.00894597647692e+00 7.44916062932100e-01 2.80438654155822e+01\n3.44654515719602e-01\t1.39999999999997e+00 6.46643966816555e-01 -1.50948776993374e-13 2.92373525631135e+00\t4.85511532746309e+00 1.99732689263929e+00 7.41373383420612e-01 2.80120923057265e+01\n3.45904987662027e-01\t1.39999999999998e+00 6.41330036317393e-01 -1.51547308114052e-13 2.92570674347447e+00\t4.85309825727345e+00 1.98577982356288e+00 7.37881951888863e-01 2.79808059154678e+01\n3.47165317982707e-01\t1.39999999999997e+00 6.35889477768294e-01 -1.55250533364070e-13 2.92768059230997e+00\t4.85101578247417e+00 1.97419246315734e+00 7.34439221334987e-01 2.79504281172142e+01\n3.48434253757069e-01\t1.39999999999997e+00 6.30352874585603e-01 -1.60251320577487e-13 2.92964348479526e+00\t4.84888927859043e+00 1.96251866415745e+00 7.31014376559097e-01 2.79212179664036e+01\n3.49712311531314e-01\t1.39999999999997e+00 6.24707350768931e-01 -1.70522546928379e-13 2.93160618750756e+00\t4.84669252574621e+00 1.95083050595784e+00 7.27569895571773e-01 2.78931081945551e+01\n3.51000274219781e-01\t1.39999999999997e+00 6.18913626964191e-01 -1.82378830936438e-13 2.93356867182134e+00\t4.84443351546135e+00 1.93923520778452e+00 7.24157468340587e-01 2.78661816744825e+01\n3.52297983161380e-01\t1.39999999999998e+00 6.12983115377660e-01 -1.91675882752038e-13 2.93553899117493e+00\t4.84206520223087e+00 1.92761889963430e+00 7.20806590461753e-01 2.78400813045170e+01\n3.53604081918852e-01\t1.39999999999997e+00 6.06943520911618e-01 -1.91786223236054e-13 2.93751357660155e+00\t4.83961070631953e+00 1.91589569177659e+00 7.17499698499653e-01 2.78147626826944e+01\n3.54904891356314e-01\t1.39999999999997e+00 6.00842803927996e-01 -1.84372822944162e-13 2.93944647710507e+00\t4.83726637010398e+00 1.90419356687429e+00 7.14267558373611e-01 2.77909404167206e+01\n3.56195779753565e-01\t1.39999999999997e+00 5.94730548929542e-01 -1.83294426607581e-13 2.94133344174503e+00\t4.83498391339491e+00 1.89283280937098e+00 7.11154046086603e-01 2.77683527909258e+01\n3.57475191593847e-01\t1.39999999999998e+00 5.88584391238931e-01 -1.86852530172994e-13 2.94317889348356e+00\t4.83275329645966e+00 1.88172267663460e+00 7.08149857501776e-01 2.77471070526689e+01\n3.58746787445599e-01\t1.39999999999998e+00 5.82389387816764e-01 -1.83118804650878e-13 2.94498491174227e+00\t4.83056066466674e+00 1.87069484023023e+00 7.05224421541667e-01 2.77271081821077e+01\n3.60010527750965e-01\t1.39999999999997e+00 5.76147958865196e-01 -1.85541727781397e-13 2.94674631760163e+00\t4.82836146702128e+00 1.85966283001082e+00 7.02362457080450e-01 2.77081584327852e+01\n3.61264064940366e-01\t1.39999999999997e+00 5.69878760449142e-01 -1.86728182634255e-13 2.94845765649961e+00\t4.82614330818333e+00 1.84890480166348e+00 6.99601320861840e-01 2.76902168018012e+01\n3.62505164025846e-01\t1.39999999999997e+00 5.63587732705583e-01 -1.86615173286871e-13 2.95012217866447e+00\t4.82392019013125e+00 1.83849404769174e+00 6.96969711933499e-01 2.76732425891618e+01\n3.63739695862364e-01\t1.39999999999998e+00 5.57246381908032e-01 -1.89010085890313e-13 2.95175348386312e+00\t4.82168902000052e+00 1.82820746879272e+00 6.94436986167669e-01 2.76570794828825e+01\n3.64973856209500e-01\t1.39999999999997e+00 5.50833228576472e-01 -1.87492043571429e-13 2.95335809612564e+00\t4.81948659017902e+00 1.81793522387268e+00 6.91981580979160e-01 2.76418062875671e+01\n3.66197039315943e-01\t1.39999999999998e+00 5.44378061781522e-01 -1.85565091978558e-13 2.95493137267359e+00\t4.81733033407513e+00 1.80770813634006e+00 6.89574810427538e-01 2.76275690747796e+01\n3.67409300577157e-01\t1.39999999999997e+00 5.37888151422615e-01 -1.79816747179897e-13 2.95647179090199e+00\t4.81520572182900e+00 1.79772250766635e+00 6.87238023135221e-01 2.76142679605732e+01\n3.68614307038340e-01\t1.39999999999997e+00 5.31335638469415e-01 -1.85345449896937e-13 2.95797206297608e+00\t4.81310397952636e+00 1.78792056414122e+00 6.85005052666408e-01 2.76018835247765e+01\n3.69820194173687e-01\t1.39999999999997e+00 5.24719942019615e-01 -1.99982571316159e-13 2.95944234287001e+00\t4.81099751347171e+00 1.77830768773987e+00 6.82897183918005e-01 2.75901975533808e+01\n3.71016779569948e-01\t1.39999999999997e+00 5.18101804098044e-01 -2.29568451419159e-13 2.96087533730153e+00\t4.80889853518050e+00 1.76891530317527e+00 6.80902182267029e-01 2.75793545568043e+01\n3.72204700379111e-01\t1.39999999999997e+00 5.11425579413471e-01 -2.46324245544968e-13 2.96227950410998e+00\t4.80679370004393e+00 1.75973396791282e+00 6.78965614366596e-01 2.75692718007839e+01\n3.73385217959534e-01\t1.39999999999997e+00 5.04706819273444e-01 -2.63362258538050e-13 2.96365495073476e+00\t4.80466786992189e+00 1.75072082281182e+00 6.77127934058735e-01 2.75599372640753e+01\n3.74560624739365e-01\t1.39999999999998e+00 4.97952078376438e-01 -2.76253434618611e-13 2.96500094224000e+00\t4.80255857454674e+00 1.74187851886425e+00 6.75412935474062e-01 2.75516719359081e+01\n3.75725649508641e-01\t1.39999999999998e+00 4.91176450530299e-01 -2.84716002715247e-13 2.96631369499579e+00\t4.80047669654080e+00 1.73318686894927e+00 6.73797568930138e-01 2.75445923689237e+01\n3.76884144383249e-01\t1.39999999999997e+00 4.84348250739633e-01 -2.97967496844666e-13 2.96760550791521e+00\t4.79841872960553e+00 1.72467649150990e+00 6.72267836368343e-01 2.75385108108670e+01\n3.78036044264208e-01\t1.39999999999997e+00 4.77479633986045e-01 -3.24975896178030e-13 2.96887176763832e+00\t4.79640204250787e+00 1.71636836965877e+00 6.70786534006356e-01 2.75333363644149e+01\n3.79189429015791e-01\t1.39999999999997e+00 4.70534118189904e-01 -3.52063544632768e-13 2.97011130161414e+00\t4.79445665483511e+00 1.70822097890843e+00 6.69368985117839e-01 2.75290194501329e+01\n3.80331990475495e-01\t1.39999999999997e+00 4.63581907690690e-01 -3.65287189441621e-13 2.97131670027718e+00\t4.79261110795375e+00 1.70033469786950e+00 6.68023799147515e-01 2.75255694943252e+01\n3.81467814888531e-01\t1.39999999999997e+00 4.56575649775016e-01 -3.75228515621314e-13 2.97250086102366e+00\t4.79079749103906e+00 1.69262898081262e+00 6.66704981032226e-01 2.75224993929867e+01\n3.82596341674202e-01\t1.39999999999996e+00 4.49508398901466e-01 -3.78921287524610e-13 2.97366266115608e+00\t4.78897535609179e+00 1.68503889203688e+00 6.65423289713220e-01 2.75194386425939e+01\n3.83726306313972e-01\t1.39999999999997e+00 4.42337387348916e-01 -3.66577726383457e-13 2.97480929302182e+00\t4.78704088329678e+00 1.67750017069735e+00 6.64231331990676e-01 2.75159165589523e+01\n3.84853677244191e-01\t1.39999999999997e+00 4.35108370943390e-01 -3.97974872703842e-13 2.97592589694840e+00\t4.78507515768115e+00 1.67011655177578e+00 6.63147597690291e-01 2.75124818503580e+01\n3.85974373462214e-01\t1.39999999999996e+00 4.27849386775342e-01 -4.25047668665260e-13 2.97701623593189e+00\t4.78309141862535e+00 1.66297589788769e+00 6.62150732208995e-01 2.75091086885183e+01\n3.87085938121508e-01\t1.39999999999997e+00 4.20582370890601e-01 -4.40524923196104e-13 2.97807365836131e+00\t4.78107450024227e+00 1.65604695418296e+00 6.61254045802912e-01 2.75056635864373e+01\n3.88197267919026e-01\t1.39999999999997e+00 4.13242781467192e-01 -4.46654997850804e-13 2.97910644068829e+00\t4.77896519506128e+00 1.64929036350837e+00 6.60444812163415e-01 2.75020151165810e+01\n3.89311935723786e-01\t1.39999999999996e+00 4.05836239732611e-01 -4.57798448156336e-13 2.98010748047157e+00\t4.77681454910877e+00 1.64279026747798e+00 6.59739094831339e-01 2.74983733257281e+01\n3.90401170244592e-01\t1.39999999999997e+00 3.98544104186714e-01 -4.61759568002125e-13 2.98104798618997e+00\t4.77471062668993e+00 1.63674796422242e+00 6.59147829490560e-01 2.74949865965496e+01\n3.91465714535107e-01\t1.39999999999997e+00 3.91378696936630e-01 -4.54528975895011e-13 2.98192987652011e+00\t4.77263666743462e+00 1.63118802325630e+00 6.58676165657115e-01 2.74917448302617e+01\n3.92513172094961e-01\t1.39999999999997e+00 3.84279873817640e-01 -4.31742958428127e-13 2.98277463883895e+00\t4.77051888945552e+00 1.62599071157539e+00 6.58333970192043e-01 2.74882796161262e+01\n3.93543371186414e-01\t1.39999999999997e+00 3.77267078009423e-01 -4.27019321032766e-13 2.98357773480145e+00\t4.76837332362664e+00 1.62110383207116e+00 6.58112289540255e-01 2.74846149070656e+01\n3.94562412032970e-01\t1.39999999999997e+00 3.70277383593923e-01 -4.36174901595670e-13 2.98434674649687e+00\t4.76620006521797e+00 1.61652021227342e+00 6.57975685267712e-01 2.74806745082221e+01\n3.95568497082271e-01\t1.39999999999997e+00 3.63325202673393e-01 -4.46108027737444e-13 2.98508413061602e+00\t4.76400416432430e+00 1.61228286257973e+00 6.57906611076114e-01 2.74764019959402e+01\n3.96562748004463e-01\t1.39999999999997e+00 3.56413015481482e-01 -4.47702972727358e-13 2.98579058511698e+00\t4.76177642504541e+00 1.60828388875315e+00 6.57911553538684e-01 2.74718242320704e+01\n3.97534685801829e-01\t1.39999999999997e+00 3.49619024288151e-01 -4.40029850832556e-13 2.98645484021137e+00\t4.75955082684776e+00 1.60458723869107e+00 6.58039791703806e-01 2.74670943243435e+01\n3.98493287233004e-01\t1.39999999999997e+00 3.42859131066242e-01 -4.46562723243249e-13 2.98708632097207e+00\t4.75730654021352e+00 1.60117216572838e+00 6.58252536386214e-01 2.74621001617107e+01\n3.99446302951460e-01\t1.39999999999997e+00 3.36102171491977e-01 -4.42927165322298e-13 2.98768732180762e+00\t4.75502791826766e+00 1.59803426790476e+00 6.58536176012740e-01 2.74568854314135e+01\n4.00396075408818e-01\t1.39999999999997e+00 3.29322868017179e-01 -4.38369878718008e-13 2.98826718800402e+00\t4.75269375060724e+00 1.59513634809543e+00 6.58930092466314e-01 2.74513502176884e+01\n4.01340000272931e-01\t1.39999999999997e+00 3.22500387501302e-01 -4.20557436805291e-13 2.98883043125647e+00\t4.75030847841621e+00 1.59245142147793e+00 6.59419127373961e-01 2.74455154957477e+01\n4.02284863657842e-01\t1.39999999999997e+00 3.15599760599249e-01 -4.20852330086784e-13 2.98937961961829e+00\t4.74785005797636e+00 1.58995767526228e+00 6.59949390190651e-01 2.74393252412089e+01\n4.03228508975463e-01\t1.39999999999996e+00 3.08670139400277e-01 -4.20304106144997e-13 2.98990963713189e+00\t4.74533978011134e+00 1.58776083438150e+00 6.60574770536747e-01 2.74329383495093e+01\n4.04177324130718e-01\t1.39999999999997e+00 3.01635232967396e-01 -4.21850570963156e-13 2.99042664276838e+00\t4.74276604341884e+00 1.58582237885579e+00 6.61286572158851e-01 2.74262983195235e+01\n4.05117765186730e-01\t1.39999999999998e+00 2.94613698130297e-01 -4.29776067026280e-13 2.99091656734602e+00\t4.74018453827603e+00 1.58418612586326e+00 6.62069826451115e-01 2.74196148397569e+01\n4.06053955551748e-01\t1.39999999999996e+00 2.87580208287531e-01 -4.40620009961704e-13 2.99137733519675e+00\t4.73758044617591e+00 1.58283266980389e+00 6.62917950030890e-01 2.74128646538600e+01\n4.06989184544076e-01\t1.39999999999997e+00 2.80492489912509e-01 -4.59926334710685e-13 2.99181630530967e+00\t4.73492618577083e+00 1.58177932001249e+00 6.63849787356290e-01 2.74058240436091e+01\n4.07919701522092e-01\t1.39999999999997e+00 2.73379547421400e-01 -4.62129182293183e-13 2.99223400796440e+00\t4.73224439970673e+00 1.58101712133627e+00 6.64868134008206e-01 2.73985776731146e+01\n4.08849039944786e-01\t1.39999999999997e+00 2.66253782501920e-01 -4.63799271011961e-13 2.99262678571697e+00\t4.72954669362779e+00 1.58057519245398e+00 6.65942211192541e-01 2.73913601554628e+01\n4.09766333867761e-01\t1.39999999999997e+00 2.59168669617040e-01 -4.61986890678459e-13 2.99300054378615e+00\t4.72683224372568e+00 1.58038241047614e+00 6.67051057555163e-01 2.73840744006468e+01\n4.10675294867225e-01\t1.39999999999997e+00 2.52076833745364e-01 -4.53361622222659e-13 2.99336949876730e+00\t4.72406334250060e+00 1.58039663411522e+00 6.68241343169776e-01 2.73764214961309e+01\n4.11583538381117e-01\t1.39999999999997e+00 2.44948341991051e-01 -4.46146376975502e-13 2.99372452066940e+00\t4.72120785847000e+00 1.58063558406978e+00 6.69476150698376e-01 2.73682856122889e+01\n4.12483729339670e-01\t1.39999999999997e+00 2.37849045890587e-01 -4.43749228975091e-13 2.99406110236015e+00\t4.71830619651208e+00 1.58115036373948e+00 6.70777864580711e-01 2.73598195670727e+01\n4.13371817624940e-01\t1.39999999999997e+00 2.30783681820875e-01 -4.37411506003274e-13 2.99437462822646e+00\t4.71538934510571e+00 1.58195053698569e+00 6.72134500138720e-01 2.73512618672393e+01\n4.14255720107356e-01\t1.39999999999997e+00 2.23700057223444e-01 -4.30856956690248e-13 2.99466510399697e+00\t4.71243283885176e+00 1.58312563628570e+00 6.73542850937992e-01 2.73427190145757e+01\n4.15142436961034e-01\t1.39999999999997e+00 2.16533993368832e-01 -4.22535444291743e-13 2.99494250089020e+00\t4.70939765297081e+00 1.58459178795635e+00 6.75025721215468e-01 2.73340613024142e+01\n4.16014670436359e-01\t1.39999999999997e+00 2.09442548113076e-01 -4.17834074889260e-13 2.99519702587688e+00\t4.70637065431140e+00 1.58629124476457e+00 6.76558427801813e-01 2.73256615378207e+01\n4.16878341124104e-01\t1.39999999999997e+00 2.02360491293793e-01 -4.10110839263381e-13 2.99543537980725e+00\t4.70335223154107e+00 1.58833860204102e+00 6.78164523718967e-01 2.73175143724885e+01\n4.17741164106221e-01\t1.39999999999997e+00 1.95226064383816e-01 -4.03031827848459e-13 2.99566315784463e+00\t4.70032077700958e+00 1.59071444923714e+00 6.79857686975207e-01 2.73094880766577e+01\n4.18603682917134e-01\t1.39999999999997e+00 1.88032695984684e-01 -3.94023493313753e-13 2.99587803416669e+00\t4.69728916438177e+00 1.59337751363354e+00 6.81642353306851e-01 2.73016227066312e+01\n4.19453140055196e-01\t1.39999999999997e+00 1.80903229599714e-01 -3.85871322982712e-13 2.99607155292420e+00\t4.69430578833999e+00 1.59629591276911e+00 6.83470639564035e-01 2.72940777038057e+01\n4.20297262746797e-01\t1.39999999999996e+00 1.73779275401205e-01 -3.73994823678029e-13 2.99624569866750e+00\t4.69132918253743e+00 1.59953039866614e+00 6.85366228292280e-01 2.72866585791001e+01\n4.21143280773738e-01\t1.39999999999998e+00 1.66584176248330e-01 -3.62689661746608e-13 2.99640502222131e+00\t4.68832108362581e+00 1.60306413346563e+00 6.87327770721101e-01 2.72792048261119e+01\n4.21983304821839e-01\t1.39999999999997e+00 1.59365950736096e-01 -3.57561833054516e-13 2.99655009258293e+00\t4.68531254278740e+00 1.60683061584558e+00 6.89315967796885e-01 2.72718289998869e+01\n4.22813372615973e-01\t1.39999999999997e+00 1.52185341904339e-01 -3.49174637958227e-13 2.99668065189013e+00\t4.68232535275907e+00 1.61087437287176e+00 6.91330751177205e-01 2.72646202398895e+01\n4.23641069258658e-01\t1.39999999999996e+00 1.45001224999228e-01 -3.25225655218488e-13 2.99679902465747e+00\t4.67933478589390e+00 1.61525442547755e+00 6.93408182951090e-01 2.72575273147547e+01\n4.24469911839580e-01\t1.39999999999997e+00 1.37764684309409e-01 -3.08291061576502e-13 2.99690877606502e+00\t4.67634377615147e+00 1.61987722557798e+00 6.95518355474397e-01 2.72505702236718e+01\n4.25285584251405e-01\t1.39999999999997e+00 1.30593602745993e-01 -2.97439470287668e-13 2.99701144909761e+00\t4.67339422235132e+00 1.62460656765507e+00 6.97629622908403e-01 2.72437550789171e+01\n4.26094762912978e-01\t1.39999999999997e+00 1.23429549247603e-01 -2.96744419454614e-13 2.99710789137133e+00\t4.67045953311061e+00 1.62953640001091e+00 6.99767646192205e-01 2.72369747627784e+01\n4.26904239179257e-01\t1.39999999999996e+00 1.16216765094873e-01 -2.89054976296049e-13 2.99719731232747e+00\t4.66751366039650e+00 1.63480941300545e+00 7.01943612086051e-01 2.72301406833224e+01\n4.27703032700962e-01\t1.39999999999996e+00 1.09055798488476e-01 -2.80830872036919e-13 2.99727738197105e+00\t4.66456738086367e+00 1.64021500718770e+00 7.04121542041202e-01 2.72230352421950e+01\n4.28491643393131e-01\t1.39999999999996e+00 1.01919036616066e-01 -2.76292328376442e-13 2.99735004871080e+00\t4.66157239108279e+00 1.64565919876630e+00 7.06286643652884e-01 2.72152863629607e+01\n4.29277075939596e-01\t1.39999999999997e+00 9.47508098625035e-02 -2.72303928806430e-13 2.99741198869744e+00\t4.65852415908593e+00 1.65129706565810e+00 7.08490129069648e-01 2.72069951232843e+01\n4.30060643094219e-01\t1.39999999999997e+00 8.75400282200118e-02 -2.65789917557659e-13 2.99746584897466e+00\t4.65547584936877e+00 1.65730603408054e+00 7.10775406915136e-01 2.71986068740566e+01\n4.30832043063015e-01\t1.39999999999997e+00 8.04129193936377e-02 -2.48862997345651e-13 2.99751596046666e+00\t4.65246745964640e+00 1.66355226723311e+00 7.13092642437089e-01 2.71902347545739e+01\n4.31596211778843e-01\t1.39999999999997e+00 7.33243145017657e-02 -2.29938685459697e-13 2.99756494126332e+00\t4.64947815822899e+00 1.66995691532895e+00 7.15402784435135e-01 2.71818316900205e+01\n4.32359669382542e-01\t1.39999999999997e+00 6.61828137723544e-02 -2.18666345174150e-13 2.99761557850171e+00\t4.64648666218226e+00 1.67657078765120e+00 7.17703814240422e-01 2.71732770598649e+01\n4.33114794520225e-01\t1.39999999999997e+00 5.90742796153422e-02 -2.21906789516187e-13 2.99766702957333e+00\t4.64351960547148e+00 1.68339210346261e+00 7.19971865448485e-01 2.71646236500319e+01\n4.33860011537965e-01\t1.39999999999997e+00 5.20546749358622e-02 -2.27873329120228e-13 2.99772317613825e+00\t4.64058333318124e+00 1.69039049500032e+00 7.22202538930335e-01 2.71558808461499e+01\n4.34600961299985e-01\t1.39999999999996e+00 4.50660251020653e-02 -2.24735920816023e-13 2.99778871026847e+00\t4.63766892465208e+00 1.69756698469723e+00 7.24373701930638e-01 2.71471302814859e+01\n4.35337900476879e-01\t1.39999999999997e+00 3.80841409758473e-02 -2.18441845402963e-13 2.99786347998702e+00\t4.63478469071189e+00 1.70478433293886e+00 7.26416045616279e-01 2.71384334594712e+01\n4.36064701783332e-01\t1.39999999999996e+00 3.11619709891623e-02 -2.15996442919916e-13 2.99794524854634e+00\t4.63198245144133e+00 1.71196889588955e+00 7.28333959184696e-01 2.71299270718415e+01\n4.36784634404837e-01\t1.39999999999997e+00 2.42906209446737e-02 -2.04058054644034e-13 2.99803155427835e+00\t4.62924167962031e+00 1.71929651230226e+00 7.30160045626085e-01 2.71213870351108e+01\n4.37502831491820e-01\t1.39999999999997e+00 1.74554368024308e-02 -1.83996169302073e-13 2.99812944290646e+00\t4.62653023680132e+00 1.72689753411794e+00 7.31929974883213e-01 2.71127158055291e+01\n4.38211365232308e-01\t1.39999999999997e+00 1.07358290988180e-02 -1.70553065112312e-13 2.99823855093182e+00\t4.62389380198736e+00 1.73453460523737e+00 7.33583352854557e-01 2.71041339076081e+01\n4.38912337795490e-01\t1.39999999999997e+00 4.04156511975144e-03 -1.69746796115342e-13 2.99835818592951e+00\t4.62133254493384e+00 1.74211897579021e+00 7.35077428682725e-01 2.70956729023839e+01\n4.39609059460473e-01\t1.39999999999997e+00 -2.69495097807382e-03 -1.72891240188211e-13 2.99849401404562e+00\t4.61883267863201e+00 1.74976346845110e+00 7.36457093385660e-01 2.70872970255571e+01\n4.40306260433823e-01\t1.39999999999996e+00 -9.43557100284488e-03 -1.85881569020845e-13 2.99864432574129e+00\t4.61637377950683e+00 1.75756092137669e+00 7.37737760420140e-01 2.70789166501387e+01\n4.41008399933776e-01\t1.39999999999997e+00 -1.62227396774303e-02 -2.00794274763772e-13 2.99880836356218e+00\t4.61393798043197e+00 1.76545525639307e+00 7.38887742794932e-01 2.70704602169748e+01\n4.41704967844728e-01\t1.39999999999997e+00 -2.29922355309764e-02 -2.04241550643265e-13 2.99898436693758e+00\t4.61156012746547e+00 1.77341720767069e+00 7.39880347606456e-01 2.70620483137528e+01\n4.42375250800207e-01\t1.39999999999997e+00 -2.95483688494290e-02 -2.00957788320229e-13 2.99916717639614e+00\t4.60930760004707e+00 1.78131428141833e+00 7.40758547513029e-01 2.70539415174865e+01\n4.43028368193501e-01\t1.39999999999996e+00 -3.59279079316252e-02 -2.08018389951464e-13 2.99935944548598e+00\t4.60713683914092e+00 1.78928667053041e+00 7.41570409939764e-01 2.70459987468238e+01\n4.43670480075122e-01\t1.39999999999996e+00 -4.21941511401806e-02 -2.00398960572310e-13 2.99956025868554e+00\t4.60502143920448e+00 1.79722626450128e+00 7.42316812504280e-01 2.70381006828943e+01\n4.44305688431511e-01\t1.39999999999997e+00 -4.84187485379189e-02 -1.83103421967252e-13 2.99977305993809e+00\t4.60293945797763e+00 1.80520664860136e+00 7.43021154853968e-01 2.70301332519884e+01\n4.44937044046352e-01\t1.39999999999997e+00 -5.46423983069372e-02 -1.66157500881012e-13 2.99999653014099e+00\t4.60087676854217e+00 1.81332101953746e+00 7.43681456601306e-01 2.70220052166924e+01\n4.45568445882106e-01\t1.39999999999997e+00 -6.08681222414150e-02 -1.59549051275056e-13 3.00022741577563e+00\t4.59881634082244e+00 1.82162961714201e+00 7.44309503021568e-01 2.70136389298878e+01\n4.46202873265854e-01\t1.39999999999997e+00 -6.71234052548249e-02 -1.60392978113264e-13 3.00046440486915e+00\t4.59673886775842e+00 1.83006041681458e+00 7.44862662060501e-01 2.70049731998062e+01\n4.46842202960184e-01\t1.39999999999997e+00 -7.34557146789062e-02 -1.62536286757275e-13 3.00070796576303e+00\t4.59462294719591e+00 1.83864597002537e+00 7.45330627091392e-01 2.69958837045738e+01\n4.47488483955261e-01\t1.39999999999996e+00 -7.99102644768257e-02 -1.60562137480014e-13 3.00095793839832e+00\t4.59246092423251e+00 1.84748371552095e+00 7.45737880965826e-01 2.69863261076285e+01\n4.48136153181383e-01\t1.39999999999997e+00 -8.63817919439289e-02 -1.58646996950933e-13 3.00120673142712e+00\t4.59027433355358e+00 1.85649503904877e+00 7.46114071975989e-01 2.69764327203042e+01\n4.48787571311795e-01\t1.39999999999996e+00 -9.28861340357579e-02 -1.58116469901814e-13 3.00145313288485e+00\t4.58804929558088e+00 1.86565491885254e+00 7.46463454330198e-01 2.69662166416010e+01\n4.49444370249278e-01\t1.39999999999997e+00 -9.94731867146392e-02 -1.58452044542240e-13 3.00169881978304e+00\t4.58575252933108e+00 1.87497649246110e+00 7.46784341277974e-01 2.69554046563110e+01\n4.50104062182681e-01\t1.39999999999996e+00 -1.06104720729157e-01 -1.58024460389412e-13 3.00193548345064e+00\t4.58339292214970e+00 1.88439173087062e+00 7.47121584438421e-01 2.69439165539645e+01\n4.50764676834835e-01\t1.39999999999997e+00 -1.12718343210485e-01 -1.58812187055117e-13 3.00215956626492e+00\t4.58096696610012e+00 1.89395387376381e+00 7.47506935644349e-01 2.69316573570231e+01\n4.51429558800303e-01\t1.39999999999997e+00 -1.19355999534628e-01 -1.59011423974668e-13 3.00237305010516e+00\t4.57844294418058e+00 1.90370208485615e+00 7.47947435196663e-01 2.69183302705540e+01\n4.52100188385239e-01\t1.39999999999997e+00 -1.26027636628783e-01 -1.57981077925809e-13 3.00257861284760e+00\t4.57576092674368e+00 1.91355442069992e+00 7.48442315933164e-01 2.69032628111089e+01\n4.52772253742325e-01\t1.39999999999997e+00 -1.32707901856752e-01 -1.59807210965981e-13 3.00276890635753e+00\t4.57295744182875e+00 1.92337662234716e+00 7.48981839950316e-01 2.68867543135407e+01\n4.53445432722409e-01\t1.39999999999996e+00 -1.39399131484952e-01 -1.58340346341154e-13 3.00293715632121e+00\t4.57009369165333e+00 1.93331760905960e+00 7.49584353285437e-01 2.68695268042934e+01\n4.54123104668016e-01\t1.39999999999997e+00 -1.46139037076510e-01 -1.64348455731128e-13 3.00308579627212e+00\t4.56713436767754e+00 1.94337011349380e+00 7.50236689975620e-01 2.68514565120024e+01\n4.54807154211707e-01\t1.39999999999997e+00 -1.52947669472187e-01 -1.75561257078187e-13 3.00321101724675e+00\t4.56407096810567e+00 1.95346097124411e+00 7.50914091990096e-01 2.68324073897097e+01\n4.55487894029124e-01\t1.39999999999996e+00 -1.59727024122821e-01 -1.86525800396535e-13 3.00330903116991e+00\t4.56094668836463e+00 1.96345115952740e+00 7.51610935443448e-01 2.68125413883165e+01\n4.56164068832809e-01\t1.39999999999997e+00 -1.66456819893797e-01 -2.07363275345021e-13 3.00338154790475e+00\t4.55776535417815e+00 1.97347238305990e+00 7.52346404401600e-01 2.67919733269998e+01\n4.56836474231303e-01\t1.39999999999996e+00 -1.73153531833924e-01 -2.22794779467717e-13 3.00343064010722e+00\t4.55451234449601e+00 1.98356226513224e+00 7.53123772117841e-01 2.67706510417120e+01\n4.57506036076826e-01\t1.39999999999997e+00 -1.79825270276342e-01 -2.41152941247733e-13 3.00346253289395e+00\t4.55114348222956e+00 1.99360337304505e+00 7.53933538488699e-01 2.67483169725401e+01\n4.58173758264960e-01\t1.39999999999997e+00 -1.86470090618579e-01 -2.51571560503867e-13 3.00346883304457e+00\t4.54768866581017e+00 2.00364903466088e+00 7.54799705377761e-01 2.67251924230704e+01\n4.58840331145427e-01\t1.39999999999996e+00 -1.93107231391358e-01 -2.64357121242401e-13 3.00344531141675e+00\t4.54417738967534e+00 2.01387608906500e+00 7.55748790450380e-01 2.67014278642080e+01\n4.59506685165805e-01\t1.39999999999996e+00 -1.99730259735696e-01 -2.61175212776109e-13 3.00339293169626e+00\t4.54061299240164e+00 2.02427788251369e+00 7.56786611028492e-01 2.66770381506819e+01\n4.60173871716786e-01\t1.39999999999997e+00 -2.06333019302057e-01 -2.49620940212645e-13 3.00331432385190e+00\t4.53697366419195e+00 2.03474521991012e+00 7.57896033253562e-01 2.66518293546428e+01\n4.60842893935276e-01\t1.39999999999996e+00 -2.12941741298954e-01 -2.42061766245366e-13 3.00321271778068e+00\t4.53323208471305e+00 2.04520879983978e+00 7.59083007310586e-01 2.66256114080314e+01\n4.61514650388624e-01\t1.39999999999997e+00 -2.19562815623543e-01 -2.35639091024012e-13 3.00308686705754e+00\t4.52938196045636e+00 2.05581241147097e+00 7.60379929590386e-01 2.65983834489711e+01\n4.62189817768481e-01\t1.39999999999997e+00 -2.26198757235017e-01 -2.24522619523880e-13 3.00293464000105e+00\t4.52543481241872e+00 2.06657780189350e+00 7.61773821266446e-01 2.65702797648448e+01\n4.62868833838768e-01\t1.39999999999996e+00 -2.32868418147750e-01 -2.10732326660084e-13 3.00276033443133e+00\t4.52139083934893e+00 2.07744170163298e+00 7.63242297035056e-01 2.65413206964239e+01\n4.63552368960810e-01\t1.39999999999996e+00 -2.39572936603645e-01 -1.98045481624858e-13 3.00256697186403e+00\t4.51725387210717e+00 2.08834626344104e+00 7.64770072110105e-01 2.65116025788978e+01\n4.64240788127357e-01\t1.39999999999997e+00 -2.46335815569199e-01 -1.98313564472197e-13 3.00235708289472e+00\t4.51304064667602e+00 2.09940692685900e+00 7.66323379036445e-01 2.64812967749763e+01\n4.64934427819109e-01\t1.39999999999997e+00 -2.53164196177237e-01 -2.07081464867580e-13 3.00213293974991e+00\t4.50875045108192e+00 2.11059189681000e+00 7.67884612223422e-01 2.64504513785532e+01\n4.65633095141668e-01\t1.39999999999997e+00 -2.60063009484213e-01 -2.16996037930990e-13 3.00189560687989e+00\t4.50435696617278e+00 2.12195114301145e+00 7.69453478783052e-01 2.64190112193731e+01\n4.66336525428370e-01\t1.39999999999996e+00 -2.67002812036576e-01 -2.02874261489810e-13 3.00164337851642e+00\t4.49986573562849e+00 2.13343980512404e+00 7.71024556474532e-01 2.63869164029903e+01\n4.67044944709579e-01\t1.39999999999996e+00 -2.73986785918772e-01 -1.91350847435139e-13 3.00137578573313e+00\t4.49532503998617e+00 2.14511490024414e+00 7.72584730824285e-01 2.63543830624219e+01\n4.67758353454212e-01\t1.39999999999996e+00 -2.81016206309345e-01 -1.84315257889856e-13 3.00109745077697e+00\t4.49073611491016e+00 2.15699197406599e+00 7.74151446353717e-01 2.63214609430507e+01\n4.68476360761067e-01\t1.39999999999996e+00 -2.88086956334885e-01 -1.73740261152203e-13 3.00081472372271e+00\t4.48608884754053e+00 2.16906583184543e+00 7.75705984050890e-01 2.62881377046598e+01\n4.69198273427594e-01\t1.39999999999996e+00 -2.95188649176416e-01 -1.60930268671161e-13 3.00052614884839e+00\t4.48139120156420e+00 2.18134599687268e+00 7.77266730384119e-01 2.62545133821426e+01\n4.69924790337393e-01\t1.39999999999996e+00 -3.02293449312857e-01 -1.53895001538902e-13 3.00023251957452e+00\t4.47666161897756e+00 2.19373274004327e+00 7.78845691928362e-01 2.62208282244016e+01\n4.70654394187004e-01\t1.39999999999996e+00 -3.09384119042050e-01 -1.45651667134056e-13 2.99993970181654e+00\t4.47190715669905e+00 2.20610230459033e+00 7.80413985148977e-01 2.61871849370874e+01\n4.71388016772051e-01\t1.39999999999996e+00 -3.16446716807440e-01 -1.38907209384982e-13 2.99964384974328e+00\t4.46713063501646e+00 2.21847587662810e+00 7.81958350939460e-01 2.61535317043176e+01\n4.72125145539433e-01\t1.39999999999997e+00 -3.23471914859001e-01 -1.23894620087127e-13 2.99935094609880e+00\t4.46233141763171e+00 2.23071821686864e+00 7.83461425387950e-01 2.61198865829000e+01\n4.72865579257472e-01\t1.39999999999996e+00 -3.30440144782853e-01 -1.10238766289731e-13 2.99906009261960e+00\t4.45752877713377e+00 2.24252724474997e+00 7.84925479266145e-01 2.60863674827753e+01\n4.73609976337328e-01\t1.39999999999997e+00 -3.37366153629412e-01 -1.09052121571425e-13 2.99877553115065e+00\t4.45272503129805e+00 2.25405625825727e+00 7.86346751365634e-01 2.60530368857773e+01\n4.74357246920864e-01\t1.39999999999996e+00 -3.44215434255232e-01 -1.05386901652239e-13 2.99849777600483e+00\t4.44792736461770e+00 2.26521476075771e+00 7.87789763538995e-01 2.60199899594509e+01\n4.75107912344203e-01\t1.39999999999997e+00 -3.50983140805043e-01 -9.66732382922765e-14 2.99822590828599e+00\t4.44312514452560e+00 2.27603087577067e+00 7.89285674439470e-01 2.59871775655324e+01\n4.75862821793110e-01\t1.39999999999997e+00 -3.57658661406925e-01 -9.06483634250391e-14 2.99795552194507e+00\t4.43831574929023e+00 2.28646904036922e+00 7.90849853335451e-01 2.59545834937778e+01\n4.76622706290560e-01\t1.39999999999996e+00 -3.64264884110013e-01 -7.52599480118574e-14 2.99769580615229e+00\t4.43348798877720e+00 2.29648797619070e+00 7.92399019534410e-01 2.59221460774365e+01\n4.77388464846339e-01\t1.39999999999997e+00 -3.70791572567455e-01 -5.77564601708594e-14 2.99744368212490e+00\t4.42864643508950e+00 2.30600963858248e+00 7.93909401673762e-01 2.58898910613670e+01\n4.78160583941783e-01\t1.39999999999996e+00 -3.77252526258692e-01 -4.96823567801019e-14 2.99720484857813e+00\t4.42378616608584e+00 2.31504581719576e+00 7.95342099148256e-01 2.58578285167857e+01\n4.78938389369146e-01\t1.39999999999996e+00 -3.83665291915573e-01 -5.07127744762994e-14 2.99697646182596e+00\t4.41890854925493e+00 2.32379079640279e+00 7.96725786438710e-01 2.58259169399970e+01\n4.79722493887435e-01\t1.39999999999996e+00 -3.89990683207786e-01 -4.89446041331630e-14 2.99674773395868e+00\t4.41402028007750e+00 2.33222972831183e+00 7.98088001747368e-01 2.57941378984807e+01\n4.80513055344977e-01\t1.39999999999996e+00 -3.96237170322230e-01 -3.72168160447550e-14 2.99651638321492e+00\t4.40911327553119e+00 2.34041833251619e+00 7.99473563113025e-01 2.57624377102652e+01\n4.81308938933650e-01\t1.39999999999996e+00 -4.02390117800198e-01 -2.03011874533316e-14 2.99628913791780e+00\t4.40418044395442e+00 2.34821824444834e+00 8.00841542317918e-01 2.57308075571494e+01\n4.82107165211747e-01\t1.39999999999996e+00 -4.08452102293543e-01 -3.90972362734852e-15 2.99607457819659e+00\t4.39923399318712e+00 2.35568444214477e+00 8.02170662924464e-01 2.56993297687925e+01\n4.82909761375152e-01\t1.39999999999997e+00 -4.14468877348485e-01 1.01260760073512e-14 2.99585858975280e+00\t4.39428414784807e+00 2.36298181781817e+00 8.03612065999286e-01 2.56679609983728e+01\n4.83717752228717e-01\t1.39999999999996e+00 -4.20405481963755e-01 2.01485427981598e-14 2.99563539492011e+00\t4.38933844204520e+00 2.37000827029425e+00 8.05181204175067e-01 2.56367382807487e+01\n4.84526738756335e-01\t1.39999999999996e+00 -4.26228543040496e-01 1.82177903139694e-14 2.99541245217423e+00\t4.38441322400549e+00 2.37667313075782e+00 8.06788317713224e-01 2.56057744566516e+01\n4.85339499598691e-01\t1.39999999999997e+00 -4.31947817056467e-01 2.06241593268744e-14 2.99518958537413e+00\t4.37946053653577e+00 2.38302905782899e+00 8.08447430083973e-01 2.55748049384856e+01\n4.86158627441731e-01\t1.39999999999996e+00 -4.37571372864035e-01 3.01490998327838e-14 2.99496575907946e+00\t4.37444391298783e+00 2.38910720589899e+00 8.10159343619294e-01 2.55435917440363e+01\n4.86984790777772e-01\t1.39999999999996e+00 -4.43059670421905e-01 3.32746218434925e-14 2.99474327958305e+00\t4.36933734172345e+00 2.39461504641303e+00 8.11904834815287e-01 2.55119286447819e+01\n4.87815617708347e-01\t1.39999999999996e+00 -4.48392194588345e-01 3.85596877851693e-14 2.99452533234452e+00\t4.36417638193123e+00 2.39932189132983e+00 8.13632380597089e-01 2.54799028612288e+01\n4.88651648021006e-01\t1.39999999999996e+00 -4.53596798224664e-01 4.06152785923690e-14 2.99431274477776e+00\t4.35896710077691e+00 2.40329532797993e+00 8.15358743509455e-01 2.54474892432651e+01\n4.89491193269209e-01\t1.39999999999996e+00 -4.58625178649699e-01 4.29979824892904e-14 2.99410828355565e+00\t4.35370360539045e+00 2.40648232889972e+00 8.17085904479112e-01 2.54146750462655e+01\n4.90333653506483e-01\t1.39999999999996e+00 -4.63420693977029e-01 3.89473142171853e-14 2.99391258870656e+00\t4.34839089012630e+00 2.40869125531303e+00 8.18799135914711e-01 2.53815128281043e+01\n4.91179570913330e-01\t1.39999999999996e+00 -4.67957059018236e-01 3.40179844770542e-14 2.99371365779350e+00\t4.34311017251202e+00 2.40976606581891e+00 8.20471634911729e-01 2.53483870894627e+01\n4.92028995761200e-01\t1.39999999999996e+00 -4.72245365653567e-01 3.14616862431154e-14 2.99352352129792e+00\t4.33782199842154e+00 2.40967848758117e+00 8.22090784515726e-01 2.53150711833115e+01\n4.92880599757053e-01\t1.39999999999997e+00 -4.76275621583016e-01 3.22074633919924e-14 2.99334589917029e+00\t4.33251368011773e+00 2.40843405509541e+00 8.23698291517458e-01 2.52815476294484e+01\n4.93734819248326e-01\t1.39999999999996e+00 -4.80056586356184e-01 2.88913881191881e-14 2.99318472319129e+00\t4.32716703001148e+00 2.40602922402610e+00 8.25273531330646e-01 2.52477577858931e+01\n4.94592540264713e-01\t1.39999999999996e+00 -4.83608772526232e-01 1.69919043482392e-14 2.99304224995271e+00\t4.32179906296594e+00 2.40254357009151e+00 8.26795632063381e-01 2.52138390341583e+01\n4.95452568360809e-01\t1.39999999999997e+00 -4.86950202345905e-01 2.63300387478686e-15 2.99291507889665e+00\t4.31641433788042e+00 2.39806648906342e+00 8.28293363124342e-01 2.51799707273698e+01\n4.96313913003427e-01\t1.39999999999996e+00 -4.90115330267906e-01 -7.52722641893857e-15 2.99280741493775e+00\t4.31097498389616e+00 2.39272010097439e+00 8.29721026566617e-01 2.51460363577764e+01\n4.97177307000034e-01\t1.39999999999996e+00 -4.93133128456668e-01 -1.31487339338402e-14 2.99272124002855e+00\t4.30549335744085e+00 2.38663580701091e+00 8.31063976379016e-01 2.51121451021469e+01\n4.98043826458768e-01\t1.39999999999996e+00 -4.95997662313065e-01 -2.02032025528796e-14 2.99265224949724e+00\t4.30000940045257e+00 2.37989739728809e+00 8.32287799071077e-01 2.50785434710742e+01\n4.98914217244993e-01\t1.39999999999996e+00 -4.98701534552786e-01 -3.49411389424338e-14 2.99260310870968e+00\t4.29450718663397e+00 2.37247440951774e+00 8.33414330403946e-01 2.50451796863191e+01\n4.99789337733505e-01\t1.39999999999996e+00 -5.01241111767878e-01 -3.63026461556526e-14 2.99258256907544e+00\t4.28895305883380e+00 2.36431646089284e+00 8.34441859668379e-01 2.50118672913636e+01\n5.00669855083830e-01\t1.39999999999996e+00 -5.03632841474455e-01 -3.17352859355370e-14 2.99259083701171e+00\t4.28333132143649e+00 2.35558825205649e+00 8.35356196738639e-01 2.49784389021527e+01\n"
  },
  {
    "path": "tests/euler/check-mass-conservation_02.mpirun=4.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\n# time t\tprimitive state (rho, v_1, v_2, p)\t and 2nd moments\n0.00000000000000e+00\t1.40000000000000e+00 2.92532255614330e+00 4.68917502543030e-15 1.06272905283963e+00\t1.96000000000000e+00 8.75640448742187e+00 1.95631810080187e-02 1.26973153831588e+00\n1.09221806369784e-03\t1.40000000000000e+00 2.91983353850375e+00 4.06085652367298e-15 1.07219495142293e+00\t1.96151241613507e+00 8.72408650988086e+00 1.95286638716861e-02 1.30225762375569e+00\n2.18443612739568e-03\t1.40000000000000e+00 2.91523847229735e+00 3.42439888423552e-15 1.08057930813621e+00\t1.96580873970099e+00 8.69776531028807e+00 1.95360220473170e-02 1.35617002317428e+00\n3.27665419109352e-03\t1.40000000000000e+00 2.91115461442755e+00 2.91174784612956e-15 1.08850936508295e+00\t1.97262511965046e+00 8.67479860980967e+00 1.95849344877394e-02 1.43035751266704e+00\n4.36887225479136e-03\t1.40000000000000e+00 2.90725320381728e+00 2.51464963593299e-15 1.09645880870976e+00\t1.98177417759210e+00 8.65318025994665e+00 1.96668746795708e-02 1.52449411904329e+00\n5.46109031848918e-03\t1.40000000000000e+00 2.90307928333564e+00 1.85022087545398e-15 1.10498067489609e+00\t1.99302864698503e+00 8.63045488464149e+00 1.97887597685432e-02 1.63822791357328e+00\n6.55330838218692e-03\t1.40000000000000e+00 2.89843998577302e+00 9.14105406497524e-16 1.11419230813544e+00\t2.00616820006444e+00 8.60571881436077e+00 1.99497227364788e-02 1.77028507759275e+00\n7.64552644588452e-03\t1.40000000000000e+00 2.89321641668757e+00 -8.71231638999396e-18 1.12411192230939e+00\t2.02091786009183e+00 8.57854232188138e+00 2.01617124372789e-02 1.91843286344397e+00\n8.73774450958249e-03\t1.40000000000000e+00 2.88738088344062e+00 -8.25606613693902e-16 1.13463688991175e+00\t2.03701382091158e+00 8.54900245325415e+00 2.04237863965961e-02 2.08034149607511e+00\n9.82996257328433e-03\t1.40000000000000e+00 2.88096082858840e+00 -1.71197017063381e-15 1.14561221943546e+00\t2.05425135870626e+00 8.51743237940269e+00 2.07348816006266e-02 2.25423064731769e+00\n1.09221806369989e-02\t1.40000000000000e+00 2.87398314731095e+00 -3.07498914269997e-15 1.15691513939759e+00\t2.07246115767489e+00 8.48413355931726e+00 2.11204200028436e-02 2.43851695305199e+00\n1.20143987006684e-02\t1.40000000000000e+00 2.86644326619589e+00 -4.52948743686107e-15 1.16847956223352e+00\t2.09140809231457e+00 8.44925238644728e+00 2.16182937548622e-02 2.63120110783961e+00\n1.31066167624060e-02\t1.40000000000000e+00 2.85834977140756e+00 -6.81532413181475e-15 1.18023523429739e+00\t2.11090056140262e+00 8.41300885356906e+00 2.22565481734993e-02 2.83043774602286e+00\n1.41988348153087e-02\t1.40000000000000e+00 2.84976825311537e+00 -9.85317129411528e-15 1.19211261671955e+00\t2.13086781835540e+00 8.37577748615669e+00 2.30407097301951e-02 3.03526218377307e+00\n1.52910528414613e-02\t1.40000000000000e+00 2.84080905466763e+00 -1.20789388602748e-14 1.20405507044548e+00\t2.15127691886638e+00 8.33796751283073e+00 2.39498218605721e-02 3.24502242227167e+00\n1.63832707990160e-02\t1.40000000000000e+00 2.83159331301629e+00 -1.26076389022571e-14 1.21601671318494e+00\t2.17215200132535e+00 8.29987689269107e+00 2.49534146895120e-02 3.45952542339262e+00\n1.74754885946034e-02\t1.40000000000000e+00 2.82221365596654e+00 -1.17584173257166e-14 1.22796303588996e+00\t2.19354759623172e+00 8.26160829165838e+00 2.60224683840347e-02 3.67897430274100e+00\n1.85677060471345e-02\t1.40000000000000e+00 2.81270078757861e+00 -8.50643059740937e-15 1.23989669079591e+00\t2.21543677265457e+00 8.22306259207544e+00 2.71427557514642e-02 3.90297998644597e+00\n1.96599228487456e-02\t1.40000000000000e+00 2.80308659627360e+00 -5.40071907586047e-15 1.25180875223536e+00\t2.23780529749879e+00 8.18428743642117e+00 2.83148775186805e-02 4.13128286684112e+00\n2.07521385096353e-02\t1.40000000000000e+00 2.79341795311329e+00 -2.36057919872047e-15 1.26368252107720e+00\t2.26065212620518e+00 8.14546423738670e+00 2.95451706841969e-02 4.36379225500083e+00\n2.18443522980992e-02\t1.40000000000000e+00 2.78376072242567e+00 1.05648299802874e-15 1.27550399647841e+00\t2.28399199945667e+00 8.10682749488648e+00 3.08415665457448e-02 4.60060964700225e+00\n2.29365631790331e-02\t1.40000000000000e+00 2.77421086463469e+00 4.88302406510767e-15 1.28725678592171e+00\t2.30786118803559e+00 8.06861296408763e+00 3.22079199879491e-02 4.84198824294549e+00\n2.40287697568433e-02\t1.40000000000000e+00 2.76478737647148e+00 7.06798130712458e-15 1.29892802784338e+00\t2.33229588421798e+00 8.03084907826011e+00 3.36390255854998e-02 5.08818959086195e+00\n2.51209702405686e-02\t1.40000000000000e+00 2.75550310336828e+00 4.06590049631981e-15 1.31051079465854e+00\t2.35732567468172e+00 7.99353859803860e+00 3.51121492028511e-02 5.33931271394210e+00\n2.62131624647695e-02\t1.40000000000000e+00 2.74638335774207e+00 -2.16707406626902e-15 1.32200606954338e+00\t2.38295962652777e+00 7.95668499362610e+00 3.66089021137263e-02 5.59528402405738e+00\n2.73053439794887e-02\t1.40000000000000e+00 2.73737582793880e+00 -1.18826824700160e-14 1.33343382672261e+00\t2.40914806466601e+00 7.92009749992449e+00 3.81229914522964e-02 5.85540201944992e+00\n2.83975122627515e-02\t1.40000000000000e+00 2.72845019499848e+00 -1.90529188589810e-14 1.34481699242852e+00\t2.43582005614833e+00 7.88366003531262e+00 3.96450355832263e-02 6.11888211485467e+00\n2.94896650267403e-02\t1.40000000000000e+00 2.71958087220002e+00 -2.39299818655060e-14 1.35617007821277e+00\t2.46292009975183e+00 7.84731007116472e+00 4.11754603506092e-02 6.38511831272274e+00\n3.05818004597825e-02\t1.40000000000000e+00 2.71073524286044e+00 -2.72998141365621e-14 1.36750113852157e+00\t2.49039918514209e+00 7.81103898023869e+00 4.27222696512089e-02 6.65363219147806e+00\n3.16739176112855e-02\t1.40000000000000e+00 2.70190866994655e+00 -2.94806444918680e-14 1.37882186229049e+00\t2.51819515102795e+00 7.77485048832387e+00 4.42957833704964e-02 6.92381228444696e+00\n3.27660167953440e-02\t1.40000000000000e+00 2.69311678163623e+00 -3.12442007463899e-14 1.39013313117256e+00\t2.54627430018448e+00 7.73876497813771e+00 4.59013128893653e-02 7.19528155436706e+00\n3.38580992987476e-02\t1.40000000000000e+00 2.68432969894740e+00 -3.16844019955686e-14 1.40143629311065e+00\t2.57459548973751e+00 7.70268566294371e+00 4.75457478066250e-02 7.46752340272783e+00\n3.49501672600550e-02\t1.40000000000000e+00 2.67550880713368e+00 -3.19567765184979e-14 1.41273576746727e+00\t2.60310088731408e+00 7.66652840440594e+00 4.92293297676561e-02 7.73985736511022e+00\n3.60422236396978e-02\t1.40000000000000e+00 2.66663909162248e+00 -3.36937372808819e-14 1.42403535635896e+00\t2.63173815788448e+00 7.63031478095330e+00 5.09514277465046e-02 8.01179458206540e+00\n3.71342710896857e-02\t1.40000000000000e+00 2.65771276164128e+00 -3.61487763309886e-14 1.43533509721538e+00\t2.66048511452436e+00 7.59405903726165e+00 5.27114119351285e-02 8.28326212213600e+00\n3.82263117717791e-02\t1.40000000000000e+00 2.64870615181624e+00 -3.83387775456524e-14 1.44666583849069e+00\t2.68919461748958e+00 7.55770190196512e+00 5.44994851284088e-02 8.55273682709596e+00\n3.93183473095251e-02\t1.40000000000000e+00 2.63962166388283e+00 -3.79728602572726e-14 1.45804198118120e+00\t2.71777930992374e+00 7.52125283295276e+00 5.63114347179902e-02 8.81946151191348e+00\n4.04103788090166e-02\t1.40000000000000e+00 2.63046002064830e+00 -3.99097457536587e-14 1.46946089214850e+00\t2.74622622894440e+00 7.48474117935997e+00 5.81459826096129e-02 9.08357436797814e+00\n4.15024068231954e-02\t1.40000000000000e+00 2.62128856220370e+00 -4.23785410928022e-14 1.48091260214749e+00\t2.77459855952484e+00 7.44826070025154e+00 5.99958736748146e-02 9.34586540339859e+00\n4.25944315115556e-02\t1.40000000000000e+00 2.61209779997025e+00 -4.42438938861967e-14 1.49239757626499e+00\t2.80290715157843e+00 7.41173772438236e+00 6.18649828472970e-02 9.60672849808284e+00\n4.36864528904851e-02\t1.40000000000000e+00 2.60286457966412e+00 -4.37835167464307e-14 1.50392394366680e+00\t2.83110399308741e+00 7.37511130902068e+00 6.37481806127700e-02 9.86592814328746e+00\n4.47784709065568e-02\t1.40000000000000e+00 2.59358173659717e+00 -4.60707290702881e-14 1.51547723519755e+00\t2.85925307011424e+00 7.33840629523612e+00 6.56467178973860e-02 1.01242566441524e+01\n4.58704855229492e-02\t1.40000000000000e+00 2.58426304570086e+00 -4.80919864727667e-14 1.52703869712955e+00\t2.88746509281325e+00 7.30167359907709e+00 6.75604707129032e-02 1.03830071663591e+01\n4.69624967861221e-02\t1.40000000000000e+00 2.57491388027235e+00 -5.08441613660690e-14 1.53860434311156e+00\t2.91577488767131e+00 7.26493406446183e+00 6.94830786338023e-02 1.06426311482041e+01\n4.80545048755593e-02\t1.40000000000000e+00 2.56553092713937e+00 -4.96583692510940e-14 1.55016911265865e+00\t2.94419818390529e+00 7.22821502448325e+00 7.14137010650144e-02 1.09032455013891e+01\n4.91465100926524e-02\t1.40000000000000e+00 2.55612296715626e+00 -5.00050277348222e-14 1.56173009259960e+00\t2.97274966725719e+00 7.19154177719991e+00 7.33521418548995e-02 1.11648807105533e+01\n5.02385129532777e-02\t1.40000000000000e+00 2.54669818190331e+00 -5.12155811700635e-14 1.57328605305077e+00\t3.00142740032749e+00 7.15492071932552e+00 7.52935598254926e-02 1.14273712937314e+01\n5.13305141062965e-02\t1.40000000000000e+00 2.53724441211261e+00 -5.31552179242568e-14 1.58482484106262e+00\t3.03024857127089e+00 7.11833713308179e+00 7.72366449342361e-02 1.16907711594553e+01\n5.24225142328599e-02\t1.40000000000000e+00 2.52773534051696e+00 -5.41364998755509e-14 1.59633702637012e+00\t3.05921132168539e+00 7.08174443267471e+00 7.91828468466768e-02 1.19551268608334e+01\n5.35145140107350e-02\t1.40000000000000e+00 2.51817699974946e+00 -5.61348301296348e-14 1.60782153889628e+00\t3.08824500743070e+00 7.04517944530359e+00 8.11381614141076e-02 1.22197596294117e+01\n5.46065140069524e-02\t1.40000000000000e+00 2.50858976924188e+00 -5.88989271401023e-14 1.61927859073318e+00\t3.11727782637424e+00 7.00869764594195e+00 8.31053190950165e-02 1.24840415876328e+01\n5.56985147043524e-02\t1.40000000000000e+00 2.49898221831798e+00 -6.09816293002788e-14 1.63069861248567e+00\t3.14628910130038e+00 6.97232346296079e+00 8.50813653342877e-02 1.27477567843977e+01\n5.67905163579327e-02\t1.40000000000000e+00 2.48936169350376e+00 -6.29909645855921e-14 1.64207968354365e+00\t3.17523889313279e+00 6.93606792087997e+00 8.70598733192224e-02 1.30105182814035e+01\n5.78825188863038e-02\t1.40000000000000e+00 2.47970949349695e+00 -6.60916321344942e-14 1.65342638982648e+00\t3.20401597098689e+00 6.89986412826052e+00 8.90423001477504e-02 1.32711983002778e+01\n5.89745219143565e-02\t1.40000000000000e+00 2.47003314326704e+00 -6.73324494056165e-14 1.66472120060063e+00\t3.23260759773082e+00 6.86375860418125e+00 9.10168901562648e-02 1.35297393055177e+01\n6.00665248429862e-02\t1.40000000000000e+00 2.46034958175067e+00 -6.75727259207932e-14 1.67595152505152e+00\t3.26099434818406e+00 6.82781454630049e+00 9.29972622917152e-02 1.37860182976253e+01\n6.11585267816706e-02\t1.40000000000000e+00 2.45063676449303e+00 -6.52616693626053e-14 1.68712849217726e+00\t3.28899298679340e+00 6.79197806892336e+00 9.49875992575043e-02 1.40382875978781e+01\n6.22505266180335e-02\t1.40000000000000e+00 2.44091190644951e+00 -6.24150346179168e-14 1.69823200481118e+00\t3.31659439468922e+00 6.75632303163397e+00 9.69969512295246e-02 1.42863435959993e+01\n6.33425231796508e-02\t1.40000000000000e+00 2.43118166234531e+00 -6.10100589642882e-14 1.70925009443446e+00\t3.34377527018952e+00 6.72086365090388e+00 9.90309899712742e-02 1.45298567449867e+01\n6.44345153438513e-02\t1.40000000000000e+00 2.42143449852786e+00 -6.25370070473767e-14 1.72017811850218e+00\t3.37047403159065e+00 6.68557062610917e+00 1.01089040498973e-01 1.47682736103755e+01\n6.55265021681977e-02\t1.40000000000000e+00 2.41167021644093e+00 -6.37824097481779e-14 1.73101259955044e+00\t3.39668151639089e+00 6.65042919673581e+00 1.03159008531381e-01 1.50014904109586e+01\n6.66184831018493e-02\t1.40000000000000e+00 2.40188309504517e+00 -6.47288424339120e-14 1.74173709472455e+00\t3.42244290956882e+00 6.61543562244790e+00 1.05242894730919e-01 1.52301016940314e+01\n6.77104578295085e-02\t1.40000000000000e+00 2.39205732933812e+00 -6.59164687207585e-14 1.75235301210221e+00\t3.44765077022563e+00 6.58058238443404e+00 1.07337169016905e-01 1.54530471693688e+01\n6.88024262172414e-02\t1.40000000000000e+00 2.38218073391315e+00 -6.82541207710843e-14 1.76286182740165e+00\t3.47225259499609e+00 6.54583707285834e+00 1.09440496257299e-01 1.56698247795877e+01\n6.98943883084649e-02\t1.40000000000000e+00 2.37225741775273e+00 -6.87016587077492e-14 1.77325393616310e+00\t3.49625571698649e+00 6.51119180875851e+00 1.11557495631965e-01 1.58804497408943e+01\n7.09863442521570e-02\t1.40000000000000e+00 2.36230153714791e+00 -6.91088448632395e-14 1.78351239531560e+00\t3.51968250980173e+00 6.47670970346947e+00 1.13694806390062e-01 1.60850388555907e+01\n7.20782941949428e-02\t1.39999999999999e+00 2.35232824392295e+00 -6.67968712191158e-14 1.79362214847460e+00\t3.54256276052409e+00 6.44248509843538e+00 1.15847387780638e-01 1.62838348332321e+01\n7.31702382168086e-02\t1.40000000000000e+00 2.34235074351812e+00 -6.76708541159226e-14 1.80357735742799e+00\t3.56489817304629e+00 6.40858352506810e+00 1.18010666603449e-01 1.64769723214399e+01\n7.42621763730544e-02\t1.40000000000000e+00 2.33234613070205e+00 -6.81284799978812e-14 1.81338686163505e+00\t3.58658820965784e+00 6.37496319640148e+00 1.20188604474234e-01 1.66634844580547e+01\n7.53541096373068e-02\t1.40000000000000e+00 2.32234234946361e+00 -6.80606156386328e-14 1.82302859204435e+00\t3.60772353123542e+00 6.34173828979594e+00 1.22386707138597e-01 1.68442852706009e+01\n7.64460378219029e-02\t1.40000000000000e+00 2.31231745447638e+00 -6.98920362524031e-14 1.83251101971657e+00\t3.62816597827538e+00 6.30885717162426e+00 1.24608599762944e-01 1.70180874072002e+01\n7.75379605508272e-02\t1.40000000000000e+00 2.30228553217034e+00 -7.03680038530775e-14 1.84180434930735e+00\t3.64802518843066e+00 6.27641115087124e+00 1.26857421492513e-01 1.71859709851846e+01\n7.86298775183365e-02\t1.40000000000000e+00 2.29224179330587e+00 -6.90299754727616e-14 1.85090398163491e+00\t3.66725269134136e+00 6.24440044374070e+00 1.29125250699195e-01 1.73475035059540e+01\n7.97217890620801e-02\t1.40000000000000e+00 2.28219816801450e+00 -6.90739955976795e-14 1.85981836764883e+00\t3.68576970733052e+00 6.21287772893871e+00 1.31410151222072e-01 1.75022044952297e+01\n8.08136959525808e-02\t1.39999999999999e+00 2.27214348747646e+00 -6.93848877299119e-14 1.86855756802021e+00\t3.70347281825594e+00 6.18183651296533e+00 1.33719289819615e-01 1.76492099742713e+01\n8.19055991983540e-02\t1.40000000000000e+00 2.26205756497666e+00 -7.07146623368057e-14 1.87710894594367e+00\t3.72045078915793e+00 6.15126039072755e+00 1.36055092896505e-01 1.77892863042961e+01\n8.29974999902536e-02\t1.39999999999999e+00 2.25192485465932e+00 -7.34971010659891e-14 1.88547201162260e+00\t3.73678019094630e+00 6.12111283696541e+00 1.38423107619854e-01 1.79231913105963e+01\n8.40893996532546e-02\t1.39999999999999e+00 2.24175113699940e+00 -7.51020014536195e-14 1.89364196188390e+00\t3.75251733740221e+00 6.09141379488809e+00 1.40828501812476e-01 1.80514480386229e+01\n8.51812996014565e-02\t1.40000000000000e+00 2.23153287596138e+00 -7.72470654574296e-14 1.90162852705620e+00\t3.76761234134750e+00 6.06213208896087e+00 1.43274002923330e-01 1.81737119509729e+01\n8.62732013058432e-02\t1.39999999999999e+00 2.22126832104406e+00 -7.89189131183727e-14 1.90943301886269e+00\t3.78211097855504e+00 6.03325546927030e+00 1.45752983373338e-01 1.82904264901414e+01\n8.73651071431364e-02\t1.39999999999999e+00 2.21097636932566e+00 -7.96177326014438e-14 1.91707787663997e+00\t3.79586090904945e+00 6.00482131755255e+00 1.48257324236245e-01 1.84002374004761e+01\n8.84570197095890e-02\t1.40000000000000e+00 2.20065453085057e+00 -8.39289535855871e-14 1.92455613766904e+00\t3.80903942330542e+00 5.97680302488655e+00 1.50792810086479e-01 1.85049726724491e+01\n8.95489414516619e-02\t1.40000000000000e+00 2.19030081823492e+00 -9.14930783839734e-14 1.93186618589322e+00\t3.82178166549486e+00 5.94919653300156e+00 1.53367452779908e-01 1.86057133536237e+01\n9.06408745806660e-02\t1.40000000000000e+00 2.17989808517625e+00 -9.79282703954037e-14 1.93902413535728e+00\t3.83408571062967e+00 5.92198561628392e+00 1.55985311339404e-01 1.87025343558424e+01\n9.17328210910197e-02\t1.39999999999999e+00 2.16944067411468e+00 -9.90984720494703e-14 1.94603770776134e+00\t3.84579624819823e+00 5.89521319688785e+00 1.58645851299188e-01 1.87942673086240e+01\n9.28247831895561e-02\t1.40000000000000e+00 2.15895274406100e+00 -1.03221690416777e-13 1.95291561887317e+00\t3.85688561404671e+00 5.86894215982862e+00 1.61345619250326e-01 1.88808507956125e+01\n9.39167612356233e-02\t1.40000000000000e+00 2.14845672055865e+00 -1.07047772940888e-13 1.95965838286783e+00\t3.86750510876959e+00 5.84314744171666e+00 1.64077230204867e-01 1.89634812977393e+01\n9.50087533942890e-02\t1.40000000000000e+00 2.13792232307122e+00 -1.09631204022005e-13 1.96627212977486e+00\t3.87770988727504e+00 5.81772478655294e+00 1.66837232608661e-01 1.90427328289580e+01\n9.61007566822064e-02\t1.40000000000000e+00 2.12732180075660e+00 -1.10655589012281e-13 1.97276539727029e+00\t3.88739330975587e+00 5.79271403121707e+00 1.69631903589734e-01 1.91179034242206e+01\n9.71927682499056e-02\t1.39999999999999e+00 2.11665146167395e+00 -1.08314727161180e-13 1.97914123446782e+00\t3.89657128321014e+00 5.76822264662632e+00 1.72471914707158e-01 1.91894097964441e+01\n9.82847863738984e-02\t1.39999999999999e+00 2.10595815336512e+00 -1.07993288540686e-13 1.98539257006778e+00\t3.90540217973075e+00 5.74430301655386e+00 1.75356626339381e-01 1.92586205978980e+01\n9.93768086204806e-02\t1.39999999999999e+00 2.09521750303798e+00 -1.12398052290280e-13 1.99153255919884e+00\t3.91385272843015e+00 5.72075565457163e+00 1.78269507717263e-01 1.93252115764806e+01\n1.00468832102653e-01\t1.39999999999999e+00 2.08442704263361e+00 -1.04082834110483e-13 1.99756466167503e+00\t3.92193968439474e+00 5.69757722960217e+00 1.81209841891466e-01 1.93893855799502e+01\n1.01560856461994e-01\t1.40000000000000e+00 2.07360090856350e+00 -9.48716229714195e-14 2.00348209584885e+00\t3.92975642120162e+00 5.67484847655791e+00 1.84201120951483e-01 1.94518001906999e+01\n1.02652882690179e-01\t1.39999999999999e+00 2.06273675877702e+00 -8.77330260472392e-14 2.00928528125436e+00\t3.93742326460635e+00 5.65265276919526e+00 1.87222731780263e-01 1.95136616196767e+01\n1.03744913057450e-01\t1.39999999999999e+00 2.05184700614134e+00 -8.56003427035623e-14 2.01499573727912e+00\t3.94478442488304e+00 5.63095397921084e+00 1.90286607794570e-01 1.95733986864012e+01\n1.04836950361394e-01\t1.39999999999999e+00 2.04089560566708e+00 -8.62400101437750e-14 2.02062328915063e+00\t3.95179795475779e+00 5.60960054775240e+00 1.93392588456844e-01 1.96304602482221e+01\n1.05928997858609e-01\t1.39999999999999e+00 2.02986445723089e+00 -9.16063384970434e-14 2.02615378041639e+00\t3.95857779229572e+00 5.58860364349211e+00 1.96557135401529e-01 1.96863840118402e+01\n1.07021058961325e-01\t1.39999999999999e+00 2.01880613778244e+00 -9.37371876688487e-14 2.03159595983770e+00\t3.96511070083962e+00 5.56800577724940e+00 1.99772244662122e-01 1.97409252419470e+01\n1.08113136621245e-01\t1.39999999999999e+00 2.00771592109124e+00 -9.09868469474180e-14 2.03694739579660e+00\t3.97146277118337e+00 5.54787406161377e+00 2.03024086881324e-01 1.97945507713311e+01\n1.09205232171094e-01\t1.39999999999999e+00 1.99658729901779e+00 -9.37773101785395e-14 2.04222304433612e+00\t3.97761870469012e+00 5.52826012220894e+00 2.06335102674683e-01 1.98471886176578e+01\n1.10297347286810e-01\t1.39999999999999e+00 1.98544695456306e+00 -9.38066569284847e-14 2.04740782031779e+00\t3.98371788117022e+00 5.50917613323967e+00 2.09697582156648e-01 1.98999707950007e+01\n1.11389483372699e-01\t1.39999999999999e+00 1.97429875930195e+00 -9.35445996223331e-14 2.05251313845007e+00\t3.98975395765725e+00 5.49058009518169e+00 2.13116229953735e-01 1.99527207975152e+01\n1.12481641493236e-01\t1.39999999999999e+00 1.96313593894860e+00 -9.23716467104591e-14 2.05754725521839e+00\t3.99568366267698e+00 5.47245741489369e+00 2.16566179882602e-01 2.00051582513429e+01\n1.13573822337395e-01\t1.39999999999999e+00 1.95191990819423e+00 -8.85020026043981e-14 2.06251953169441e+00\t4.00142981652176e+00 5.45479945416954e+00 2.20021340252953e-01 2.00566603988126e+01\n1.14666025906356e-01\t1.39999999999999e+00 1.94067880509400e+00 -8.74005823955157e-14 2.06742607644171e+00\t4.00706143308925e+00 5.43767336247275e+00 2.23475000389361e-01 2.01078535015146e+01\n1.15758251202233e-01\t1.39999999999999e+00 1.92942524736330e+00 -9.56708633644595e-14 2.07224864047633e+00\t4.01280311501361e+00 5.42104357635245e+00 2.26903701937178e-01 2.01609477255211e+01\n1.16850496965182e-01\t1.39999999999999e+00 1.91813650192528e+00 -1.05273670198104e-13 2.07699750843351e+00\t4.01864046287461e+00 5.40487673697970e+00 2.30297063380142e-01 2.02157044052710e+01\n1.17942761821852e-01\t1.39999999999999e+00 1.90681201161358e+00 -1.10888987382939e-13 2.08169685218841e+00\t4.02445441549411e+00 5.38915698463399e+00 2.33659579951025e-01 2.02710060476140e+01\n1.19035043785845e-01\t1.39999999999999e+00 1.89542810837275e+00 -1.15429479851030e-13 2.08635088950400e+00\t4.03027020062538e+00 5.37383424201217e+00 2.36985049052181e-01 2.03272676960174e+01\n1.20127340174141e-01\t1.39999999999999e+00 1.88398181705160e+00 -1.21358440425905e-13 2.09096969402654e+00\t4.03599826458377e+00 5.35889524142140e+00 2.40285731664793e-01 2.03835611666216e+01\n1.21219647934225e-01\t1.39999999999999e+00 1.87248586195039e+00 -1.23376488027608e-13 2.09555535359628e+00\t4.04161847452791e+00 5.34442155113845e+00 2.43587020050164e-01 2.04397042041126e+01\n1.22311961494420e-01\t1.39999999999999e+00 1.86095574099233e+00 -1.21086295174460e-13 2.10010592656841e+00\t4.04718558000908e+00 5.33042223090679e+00 2.46902313468999e-01 2.04962983211408e+01\n1.23404277534410e-01\t1.39999999999999e+00 1.84939760939378e+00 -1.26041310485527e-13 2.10462119085933e+00\t4.05276098157416e+00 5.31691775994974e+00 2.50240545764912e-01 2.05536373772267e+01\n1.24496592270720e-01\t1.39999999999999e+00 1.83783925590130e+00 -1.33675363086517e-13 2.10909512964488e+00\t4.05837087628105e+00 5.30391671801282e+00 2.53586819402416e-01 2.06120075511995e+01\n1.25588901795314e-01\t1.39999999999999e+00 1.82623254611669e+00 -1.29475568043572e-13 2.11354176779509e+00\t4.06395596635097e+00 5.29125208578852e+00 2.56885517281314e-01 2.06709822045053e+01\n1.26681201657765e-01\t1.39999999999999e+00 1.81457440072592e+00 -1.31307447200310e-13 2.11798024897059e+00\t4.06938046388617e+00 5.27890787431917e+00 2.60148579337720e-01 2.07293083838867e+01\n1.27773487380487e-01\t1.39999999999999e+00 1.80291526801719e+00 -1.37461093829453e-13 2.12239836067876e+00\t4.07475804391488e+00 5.26704070052611e+00 2.63438947441452e-01 2.07882627710071e+01\n1.28865755085910e-01\t1.39999999999999e+00 1.79129412794866e+00 -1.45202216213447e-13 2.12679498299095e+00\t4.08013148221336e+00 5.25565656511651e+00 2.66758624653717e-01 2.08481517292412e+01\n1.29958000899040e-01\t1.39999999999999e+00 1.77967297776052e+00 -1.52228011567525e-13 2.13118960386552e+00\t4.08539543713526e+00 5.24460504587297e+00 2.70102874989293e-01 2.09078611687625e+01\n1.31050220430666e-01\t1.39999999999999e+00 1.76797516786399e+00 -1.43263955088157e-13 2.13557956842156e+00\t4.09059067247579e+00 5.23374685585761e+00 2.73430467471364e-01 2.09678023866286e+01\n1.32142410244611e-01\t1.39999999999999e+00 1.75620920449202e+00 -1.32745896490595e-13 2.13995906502825e+00\t4.09588642391180e+00 5.22308836188129e+00 2.76740358939744e-01 2.10295038405507e+01\n1.33234568639281e-01\t1.39999999999999e+00 1.74442690624446e+00 -1.36069874464861e-13 2.14432881749733e+00\t4.10125472438682e+00 5.21274381660655e+00 2.80073598056555e-01 2.10927073133883e+01\n1.34326695586857e-01\t1.39999999999999e+00 1.73265280748694e+00 -1.25200571953892e-13 2.14868130179615e+00\t4.10670404580677e+00 5.20270577029687e+00 2.83400552733499e-01 2.11575855271864e+01\n1.35418792398583e-01\t1.39999999999999e+00 1.72081170047981e+00 -1.20875594680712e-13 2.15301602050452e+00\t4.11227431184021e+00 5.19283650842749e+00 2.86644706286092e-01 2.12242781957851e+01\n1.36510861662084e-01\t1.39999999999999e+00 1.70891814235414e+00 -1.07673225549096e-13 2.15734377893181e+00\t4.11791930611376e+00 5.18309974447355e+00 2.89808634698696e-01 2.12923285172784e+01\n1.37602906837589e-01\t1.39999999999999e+00 1.69702980753768e+00 -1.02865861073691e-13 2.16169365165805e+00\t4.12345801163268e+00 5.17351786668961e+00 2.92871615947408e-01 2.13599371431565e+01\n1.38694931886008e-01\t1.39999999999999e+00 1.68517644801778e+00 -1.10006292169743e-13 2.16605931714485e+00\t4.12894598915741e+00 5.16398364610425e+00 2.95821474908188e-01 2.14278965801506e+01\n1.39786940832435e-01\t1.39999999999999e+00 1.67331373739021e+00 -1.09435864717682e-13 2.17045474726938e+00\t4.13436122707579e+00 5.15420419975425e+00 2.98569016233310e-01 2.14963413990576e+01\n1.40878936488536e-01\t1.39999999999999e+00 1.66150480097583e+00 -1.00602492984164e-13 2.17488014782198e+00\t4.13968414581332e+00 5.14423213694900e+00 3.01085620373126e-01 2.15650986716698e+01\n1.41970921422408e-01\t1.39999999999999e+00 1.64981082922073e+00 -9.59987215865040e-14 2.17931946107181e+00\t4.14499601025118e+00 5.13406292497203e+00 3.03350141576300e-01 2.16344634309888e+01\n1.43062898684278e-01\t1.39999999999999e+00 1.63821078284007e+00 -1.00182467625573e-13 2.18376771321467e+00\t4.15039242953484e+00 5.12350851270976e+00 3.05378482255849e-01 2.17052220341908e+01\n1.44154869459427e-01\t1.39999999999999e+00 1.62666641941892e+00 -1.05375008194009e-13 2.18822464604783e+00\t4.15591207018949e+00 5.11260058486952e+00 3.07227122231454e-01 2.17776295242951e+01\n1.45246833471082e-01\t1.39999999999999e+00 1.61521899594006e+00 -9.59409451725493e-14 2.19270124594618e+00\t4.16152628819542e+00 5.10130054387544e+00 3.08915020452513e-01 2.18513189720225e+01\n1.46338789792257e-01\t1.39999999999999e+00 1.60387844236282e+00 -8.77412798206613e-14 2.19721660235832e+00\t4.16718078658799e+00 5.08950105518011e+00 3.10448341162879e-01 2.19260535864443e+01\n1.47430732267332e-01\t1.39999999999999e+00 1.59273829202820e+00 -8.69195708221945e-14 2.20177579786988e+00\t4.17285841922634e+00 5.07731053394224e+00 3.11898602716120e-01 2.20017820272544e+01\n1.48522657026877e-01\t1.39999999999999e+00 1.58181877721498e+00 -8.70387919938471e-14 2.20638352146405e+00\t4.17856555507132e+00 5.06465459782215e+00 3.13324361839886e-01 2.20787270332901e+01\n1.49614564109156e-01\t1.39999999999999e+00 1.57112201695725e+00 -8.42838658427374e-14 2.21106447267421e+00\t4.18420700311755e+00 5.05114616664106e+00 3.14713099518929e-01 2.21558869928934e+01\n1.50706455108975e-01\t1.39999999999999e+00 1.56052580695628e+00 -8.09511755520807e-14 2.21581929867055e+00\t4.18984400614593e+00 5.03646422321998e+00 3.16012839992402e-01 2.22341548370168e+01\n1.51798332624505e-01\t1.39999999999999e+00 1.55008933137705e+00 -9.81575418793509e-14 2.22064284509143e+00\t4.19547974609540e+00 5.02087585981316e+00 3.17337536557949e-01 2.23136236801778e+01\n1.52890199928653e-01\t1.39999999999999e+00 1.53987709756306e+00 -1.08326190735378e-13 2.22553143569186e+00\t4.20116653507297e+00 5.00438197316180e+00 3.18785994452366e-01 2.23947224191893e+01\n1.53982060660259e-01\t1.39999999999999e+00 1.52992753273748e+00 -1.16833079875755e-13 2.23047305961095e+00\t4.20690075107560e+00 4.98707423606341e+00 3.20367674850313e-01 2.24771692866751e+01\n1.55073918322069e-01\t1.39999999999999e+00 1.52025175445308e+00 -1.22137504928357e-13 2.23543903724804e+00\t4.21273427770992e+00 4.96925579401590e+00 3.22064584319225e-01 2.25612035034529e+01\n1.56165775687605e-01\t1.39999999999999e+00 1.51079926655181e+00 -1.38276383226369e-13 2.24042164968372e+00\t4.21872207489758e+00 4.95097860550427e+00 3.23821675209202e-01 2.26472939817203e+01\n1.57257635857387e-01\t1.39999999999999e+00 1.50152457569565e+00 -1.53721944556925e-13 2.24544292712368e+00\t4.22486846664827e+00 4.93206815745077e+00 3.25608187226606e-01 2.27351107947971e+01\n1.58349501159885e-01\t1.39999999999999e+00 1.49238382804866e+00 -1.73701578754021e-13 2.25050487688658e+00\t4.23111561087755e+00 4.91256342018752e+00 3.27424509701646e-01 2.28239682470206e+01\n1.59441372372522e-01\t1.39999999999999e+00 1.48335959373955e+00 -2.00981216999995e-13 2.25559937920955e+00\t4.23743910118392e+00 4.89252737747592e+00 3.29310110927002e-01 2.29139409538862e+01\n1.60533248780972e-01\t1.39999999999999e+00 1.47447007081735e+00 -2.22406178631894e-13 2.26072644180967e+00\t4.24379398205224e+00 4.87205879426713e+00 3.31264656395775e-01 2.30046119035063e+01\n1.61625128479877e-01\t1.39999999999999e+00 1.46572780543471e+00 -2.42611415969194e-13 2.26590064033556e+00\t4.25013152938045e+00 4.85122977201567e+00 3.33273320996399e-01 2.30955627065841e+01\n1.62717008923964e-01\t1.39999999999999e+00 1.45713835420747e+00 -2.68559445436403e-13 2.27113040282154e+00\t4.25645817634511e+00 4.83005962778761e+00 3.35313852574494e-01 2.31868622265337e+01\n1.63808887570854e-01\t1.39999999999999e+00 1.44866514422296e+00 -2.76750857014869e-13 2.27641370417236e+00\t4.26278528065881e+00 4.80849254681259e+00 3.37393539055426e-01 2.32786103278383e+01\n1.64900762757469e-01\t1.39999999999999e+00 1.44024370407613e+00 -2.79381059478712e-13 2.28174346775679e+00\t4.26907163294596e+00 4.78657547486135e+00 3.39519264471280e-01 2.33705756855969e+01\n1.65992634542039e-01\t1.39999999999999e+00 1.43192986115545e+00 -3.02062428842641e-13 2.28709627135078e+00\t4.27543792774976e+00 4.76456513325533e+00 3.41756233212735e-01 2.34637639177121e+01\n1.67084505136532e-01\t1.39999999999999e+00 1.42374469212608e+00 -3.28557041527581e-13 2.29246145211616e+00\t4.28192857722068e+00 4.74260027058111e+00 3.44076132282001e-01 2.35580540452006e+01\n1.68176379941894e-01\t1.39999999999999e+00 1.41567205354604e+00 -3.48153333803517e-13 2.29782563529021e+00\t4.28859405053912e+00 4.72073748559951e+00 3.46466763916272e-01 2.36536076332570e+01\n1.69268268832591e-01\t1.39999999999999e+00 1.40770600635008e+00 -3.55495523805442e-13 2.30320891032874e+00\t4.29540490422966e+00 4.69890559612922e+00 3.48891868492982e-01 2.37498450895449e+01\n1.70360183985993e-01\t1.39999999999999e+00 1.39983418659642e+00 -3.59732460828787e-13 2.30862338832462e+00\t4.30229667074958e+00 4.67698880045664e+00 3.51223933680016e-01 2.38464884032129e+01\n1.71452144783598e-01\t1.39999999999999e+00 1.39205465939362e+00 -3.74985434112826e-13 2.31408030986686e+00\t4.30921505441713e+00 4.65498469203833e+00 3.53661689906254e-01 2.39433817788300e+01\n1.72544176905568e-01\t1.39999999999999e+00 1.38437441367799e+00 -3.87087300121496e-13 2.31956120159685e+00\t4.31620736226104e+00 4.63308975209024e+00 3.56333663579483e-01 2.40407177179639e+01\n1.73636312707653e-01\t1.39999999999999e+00 1.37678301397662e+00 -4.00249317471937e-13 2.32505537125979e+00\t4.32333105839389e+00 4.61126163910125e+00 3.59193736431581e-01 2.41392941527686e+01\n1.74728591489993e-01\t1.39999999999999e+00 1.36928061932096e+00 -4.17200733909058e-13 2.33056578584017e+00\t4.33061086126479e+00 4.58943412431789e+00 3.62182533972519e-01 2.42391466326283e+01\n1.75821059440838e-01\t1.39999999999999e+00 1.36186163554157e+00 -4.39023710837089e-13 2.33609088892899e+00\t4.33810954852862e+00 4.56755885297597e+00 3.65308492289547e-01 2.43411306303242e+01\n1.76913769425920e-01\t1.39999999999999e+00 1.35451663446468e+00 -4.61558346451293e-13 2.34164821509583e+00\t4.34577574219584e+00 4.54559381975561e+00 3.68452259571182e-01 2.44447425178562e+01\n1.78006773231766e-01\t1.39999999999999e+00 1.34721100044083e+00 -4.76756294579185e-13 2.34725495942235e+00\t4.35356418114230e+00 4.52333662976811e+00 3.71544512229611e-01 2.45497233911369e+01\n1.79100104488916e-01\t1.39999999999999e+00 1.33997375487722e+00 -4.79470868949120e-13 2.35290338353498e+00\t4.36147779750871e+00 4.50088090205939e+00 3.74751947040014e-01 2.46559930472262e+01\n1.80193830159202e-01\t1.39999999999999e+00 1.33282985424061e+00 -4.76437148673531e-13 2.35858259200269e+00\t4.36952497270057e+00 4.47833227734568e+00 3.78215127282470e-01 2.47635534599078e+01\n1.81288022500281e-01\t1.39999999999999e+00 1.32576195533496e+00 -4.66224479692586e-13 2.36430579573289e+00\t4.37759402028292e+00 4.45566064524680e+00 3.81885199418239e-01 2.48711132768187e+01\n1.82382759467649e-01\t1.39999999999999e+00 1.31875508394062e+00 -4.50766079158930e-13 2.37008156615374e+00\t4.38563181345623e+00 4.43276516160965e+00 3.85713536853571e-01 2.49779361135054e+01\n1.83478125942249e-01\t1.39999999999999e+00 1.31181882863102e+00 -4.45802810074441e-13 2.37588995415224e+00\t4.39371527662960e+00 4.40967440674572e+00 3.89627096553420e-01 2.50846586887586e+01\n1.84574216087252e-01\t1.39999999999999e+00 1.30496408480208e+00 -4.44269442389802e-13 2.38171437433594e+00\t4.40189675533820e+00 4.38659027881193e+00 3.93541413475108e-01 2.51921332963928e+01\n1.85671135903129e-01\t1.39999999999999e+00 1.29817786136131e+00 -4.53645728997307e-13 2.38754576477206e+00\t4.41020557052105e+00 4.36353633869302e+00 3.97409993569106e-01 2.53009872778383e+01\n1.86769003694136e-01\t1.39999999999999e+00 1.29147578555567e+00 -4.68399807532277e-13 2.39337712990829e+00\t4.41865876122289e+00 4.34056827071783e+00 4.01339240857855e-01 2.54114592241680e+01\n1.87867949499103e-01\t1.39999999999999e+00 1.28485780557321e+00 -4.76070314299215e-13 2.39920963478301e+00\t4.42724643375275e+00 4.31769428680505e+00 4.05458362938869e-01 2.55230138589243e+01\n1.88968114001685e-01\t1.39999999999999e+00 1.27833392067953e+00 -4.77299209453172e-13 2.40503984272619e+00\t4.43599910173815e+00 4.29491764768860e+00 4.09850568379048e-01 2.56353472363783e+01\n1.90069647051324e-01\t1.39999999999999e+00 1.27191301954954e+00 -4.73550162147668e-13 2.41087315386066e+00\t4.44487148258238e+00 4.27221131972305e+00 4.14517806302793e-01 2.57479662800772e+01\n1.91172706123969e-01\t1.39999999999999e+00 1.26557873424611e+00 -4.61275883983070e-13 2.41671410850074e+00\t4.45383359455975e+00 4.24948403003418e+00 4.19355262062418e-01 2.58608953863649e+01\n1.92277455593434e-01\t1.39999999999999e+00 1.25930602884221e+00 -4.46965675041021e-13 2.42255962296931e+00\t4.46290534722144e+00 4.22669147140691e+00 4.24235718018128e-01 2.59743444661937e+01\n1.93384062733848e-01\t1.39999999999999e+00 1.25307779556491e+00 -4.36309136467154e-13 2.42841588291953e+00\t4.47207332583424e+00 4.20375433619021e+00 4.29091385432125e-01 2.60881831563347e+01\n1.94492695210589e-01\t1.39999999999999e+00 1.24685870425869e+00 -4.32369335287006e-13 2.43430171644451e+00\t4.48127050494868e+00 4.18057984052878e+00 4.33915093833166e-01 2.62013552596107e+01\n1.95603518579959e-01\t1.39999999999999e+00 1.24063776832294e+00 -4.37692102058324e-13 2.44021305458413e+00\t4.49048087253864e+00 4.15720826089168e+00 4.38804922073960e-01 2.63134301149565e+01\n1.96716694386534e-01\t1.39999999999999e+00 1.23445488804118e+00 -4.45212206731793e-13 2.44615670005367e+00\t4.49960524991291e+00 4.13376561312994e+00 4.43840817098601e-01 2.64232308067250e+01\n1.97832378771678e-01\t1.39999999999999e+00 1.22834223260900e+00 -4.50685375596580e-13 2.45210319418327e+00\t4.50879309693297e+00 4.11036390314476e+00 4.49085295694779e-01 2.65324459143987e+01\n1.98950721801477e-01\t1.39999999999999e+00 1.22230951863658e+00 -4.63517242010138e-13 2.45804117370191e+00\t4.51814251171261e+00 4.08699992424758e+00 4.54548257081130e-01 2.66419465769266e+01\n2.00071869679951e-01\t1.39999999999999e+00 1.21636654313442e+00 -4.70421064934756e-13 2.46397082673881e+00\t4.52761169898926e+00 4.06380571604454e+00 4.60176548350920e-01 2.67516784788825e+01\n2.01195968623683e-01\t1.39999999999999e+00 1.21049258837204e+00 -4.75388002362988e-13 2.46989326794361e+00\t4.53719958174414e+00 4.04072969551546e+00 4.65908462436118e-01 2.68623380056407e+01\n2.02323157255617e-01\t1.39999999999999e+00 1.20466810313825e+00 -4.80736447540509e-13 2.47581485372216e+00\t4.54691955976958e+00 4.01766467441457e+00 4.71685230346105e-01 2.69734182133442e+01\n2.03453561986681e-01\t1.39999999999999e+00 1.19886143080382e+00 -4.79995442104392e-13 2.48174063076766e+00\t4.55674699279381e+00 3.99450016696377e+00 4.77459201641408e-01 2.70846536182250e+01\n2.04587297618819e-01\t1.39999999999999e+00 1.19305478714169e+00 -4.76195037986483e-13 2.48766886916729e+00\t4.56667393669658e+00 3.97124222947279e+00 4.83276187424162e-01 2.71959419122844e+01\n2.05724469094624e-01\t1.39999999999999e+00 1.18725395053412e+00 -4.82636649599463e-13 2.49358307441601e+00\t4.57672555474638e+00 3.94800152938046e+00 4.89151293963921e-01 2.73076598369198e+01\n2.06865171355194e-01\t1.39999999999999e+00 1.18147110926247e+00 -4.91613086738965e-13 2.49949752270732e+00\t4.58679530326437e+00 3.92483266797217e+00 4.95144984937086e-01 2.74185673869249e+01\n2.08009487855710e-01\t1.39999999999999e+00 1.17573308073323e+00 -4.92731931580627e-13 2.50542024655707e+00\t4.59679089563751e+00 3.90172293959711e+00 5.01276475923556e-01 2.75279489753410e+01\n2.09157489666880e-01\t1.39999999999999e+00 1.17002379692328e+00 -4.92244041862787e-13 2.51136151473309e+00\t4.60664275159258e+00 3.87859867905148e+00 5.07521763679957e-01 2.76350136883252e+01\n2.10309236084768e-01\t1.39999999999998e+00 1.16435502564806e+00 -4.97522788509188e-13 2.51731208610247e+00\t4.61642597750098e+00 3.85545027740543e+00 5.13863315942452e-01 2.77406445113462e+01\n2.11464776384187e-01\t1.39999999999999e+00 1.15872562439775e+00 -5.04547666777330e-13 2.52326966058995e+00\t4.62617881471690e+00 3.83222090307203e+00 5.20334682306715e-01 2.78453693220489e+01\n2.12624153212636e-01\t1.39999999999999e+00 1.15314016154663e+00 -5.05226310369814e-13 2.52921788998453e+00\t4.63593478348735e+00 3.80896336797155e+00 5.26872623840454e-01 2.79497624370453e+01\n2.13787404257123e-01\t1.39999999999998e+00 1.14759674597175e+00 -5.02643796374633e-13 2.53514265835314e+00\t4.64573165364461e+00 3.78573093886675e+00 5.33486135850589e-01 2.80541644650925e+01\n2.14954565157613e-01\t1.39999999999999e+00 1.14208083797049e+00 -5.03355455060805e-13 2.54104761364438e+00\t4.65552939446640e+00 3.76253599885568e+00 5.40171796792893e-01 2.81578333290697e+01\n2.16125671481103e-01\t1.39999999999999e+00 1.13659534843325e+00 -4.91704795332543e-13 2.54694261307978e+00\t4.66529223252865e+00 3.73935911300309e+00 5.46947608778001e-01 2.82602571548177e+01\n2.17300761255642e-01\t1.39999999999999e+00 1.13113898622877e+00 -4.76605892485716e-13 2.55283667567917e+00\t4.67498897309831e+00 3.71609130205659e+00 5.53748215470906e-01 2.83612050579533e+01\n2.18479875489244e-01\t1.39999999999998e+00 1.12569335555854e+00 -4.71514231370216e-13 2.55872635218156e+00\t4.68459913172088e+00 3.69269668985968e+00 5.60546841957349e-01 2.84605620227625e+01\n2.19663061457399e-01\t1.39999999999998e+00 1.12025476608367e+00 -4.62743221480332e-13 2.56459513802103e+00\t4.69408005236538e+00 3.66934214230600e+00 5.67376819034428e-01 2.85576436578876e+01\n2.20850374729862e-01\t1.39999999999999e+00 1.11483379715138e+00 -4.46642860791623e-13 2.57044718376671e+00\t4.70332975665924e+00 3.64605407002519e+00 5.74220864174145e-01 2.86514062394952e+01\n2.22041878199102e-01\t1.39999999999998e+00 1.10944931024911e+00 -4.26034105642576e-13 2.57628293136203e+00\t4.71238221436177e+00 3.62285870990475e+00 5.80992297601239e-01 2.87421070141210e+01\n2.23237642633269e-01\t1.39999999999999e+00 1.10408380472580e+00 -4.06940376459453e-13 2.58204903929402e+00\t4.72128998908414e+00 3.59983946674093e+00 5.87942117893827e-01 2.88299030338156e+01\n2.24437748034689e-01\t1.39999999999998e+00 1.09873059462424e+00 -3.89380014960968e-13 2.58773476170540e+00\t4.73012630131088e+00 3.57704732106088e+00 5.95119438870793e-01 2.89158437041920e+01\n2.25642382982872e-01\t1.39999999999999e+00 1.09338888640945e+00 -3.77633978295385e-13 2.59336803196231e+00\t4.73883298887586e+00 3.55438268489087e+00 6.02457681287364e-01 2.89995412921860e+01\n2.26851786682807e-01\t1.39999999999998e+00 1.08808450328943e+00 -3.64442614194998e-13 2.59894455416572e+00\t4.74745261060154e+00 3.53186729068245e+00 6.09877642211229e-01 2.90813565761995e+01\n2.28066091965857e-01\t1.39999999999998e+00 1.08280747631060e+00 -3.48287228350142e-13 2.60448059032905e+00\t4.75591735023322e+00 3.50938040652907e+00 6.17357467088108e-01 2.91603983420208e+01\n2.29285363541428e-01\t1.39999999999998e+00 1.07754833122502e+00 -3.33701893627356e-13 2.60999857791758e+00\t4.76410258932996e+00 3.48683467198011e+00 6.24852196880698e-01 2.92354193754015e+01\n2.30509651844399e-01\t1.39999999999998e+00 1.07231342521566e+00 -3.30884605632613e-13 2.61551570004517e+00\t4.77191935064153e+00 3.46419420312493e+00 6.32281698248067e-01 2.93055852914545e+01\n2.31738993381130e-01\t1.39999999999998e+00 1.06712632602401e+00 -3.14560475975571e-13 2.62099085041976e+00\t4.77937930670234e+00 3.44160398647195e+00 6.39816473494704e-01 2.93711155613333e+01\n2.32973406682462e-01\t1.39999999999998e+00 1.06197189487356e+00 -3.12014645417822e-13 2.62638568425708e+00\t4.78658375106558e+00 3.41920187364931e+00 6.47572317401273e-01 2.94327858424591e+01\n2.34212924725592e-01\t1.39999999999998e+00 1.05681373092736e+00 -3.07722683238330e-13 2.63171040551334e+00\t4.79360291069029e+00 3.39694362743598e+00 6.55456149326201e-01 2.94913507182228e+01\n2.35457600916080e-01\t1.39999999999998e+00 1.05165502752851e+00 -3.09725598922093e-13 2.63695667413347e+00\t4.80042119902946e+00 3.37482744122417e+00 6.63488984243679e-01 2.95471387420781e+01\n2.36707462102902e-01\t1.39999999999999e+00 1.04651159766616e+00 -2.99997151315245e-13 2.64211432007896e+00\t4.80703743942389e+00 3.35289469587039e+00 6.71733182797199e-01 2.96004753295696e+01\n2.37962500223532e-01\t1.39999999999998e+00 1.04137717080189e+00 -2.80012014602534e-13 2.64720033027859e+00\t4.81344758983512e+00 3.33106913320460e+00 6.80104215239048e-01 2.96509762656242e+01\n2.39222677470021e-01\t1.39999999999998e+00 1.03625765425488e+00 -2.63526477820794e-13 2.65220670301796e+00\t4.81964193379486e+00 3.30934867590633e+00 6.88504312282809e-01 2.96978258555429e+01\n2.40487898392335e-01\t1.39999999999998e+00 1.03113648926366e+00 -2.51127475968929e-13 2.65714993696757e+00\t4.82554383179154e+00 3.28769216127414e+00 6.96898825395093e-01 2.97400913077009e+01\n2.41758143629112e-01\t1.39999999999998e+00 1.02600669043169e+00 -2.45507573354415e-13 2.66203467150007e+00\t4.83111154687363e+00 3.26605903730945e+00 7.05403674966159e-01 2.97777017541029e+01\n2.43033451255392e-01\t1.39999999999998e+00 1.02087011763341e+00 -2.44766567918298e-13 2.66685672793992e+00\t4.83641799320510e+00 3.24444352252219e+00 7.14042483988742e-01 2.98118305968450e+01\n2.44313983005738e-01\t1.39999999999998e+00 1.01574535617622e+00 -2.50188379970681e-13 2.67163263036074e+00\t4.84153858003534e+00 3.22280583538410e+00 7.22834076461406e-01 2.98432803262211e+01\n2.45600001761595e-01\t1.39999999999998e+00 1.01064217970009e+00 -2.51985868404827e-13 2.67635023274457e+00\t4.84648591648493e+00 3.20121182668323e+00 7.31845939106783e-01 2.98720370885330e+01\n2.46891785447333e-01\t1.39999999999998e+00 1.00556951170150e+00 -2.45507573354415e-13 2.68100284106936e+00\t4.85122618706926e+00 3.17971117787760e+00 7.41112761380010e-01 2.98981503487278e+01\n2.48189615147917e-01\t1.39999999999998e+00 1.00048844628286e+00 -2.43856818669995e-13 2.68560100399398e+00\t4.85563296800585e+00 3.15829248535110e+00 7.50649969255667e-01 2.99204932128234e+01\n2.49493762913897e-01\t1.39999999999998e+00 9.95398725971221e-01 -2.40863450175580e-13 2.69012489860157e+00\t4.85981737428235e+00 3.13698058853778e+00 7.60347364160476e-01 2.99391517664655e+01\n2.50804470599720e-01\t1.39999999999998e+00 9.90314507943465e-01 -2.33380028939544e-13 2.69457033970718e+00\t4.86372621567361e+00 3.11581458035277e+00 7.70205518730081e-01 2.99535861957201e+01\n2.52121940214169e-01\t1.39999999999998e+00 9.85213620716948e-01 -2.29454901134367e-13 2.69897519180674e+00\t4.86715030657551e+00 3.09467692263236e+00 7.80225410461205e-01 2.99619974491445e+01\n2.53446389399840e-01\t1.39999999999998e+00 9.80103329108151e-01 -2.31971384942172e-13 2.70337084209649e+00\t4.87008750403888e+00 3.07349579906117e+00 7.90359259067490e-01 2.99645891483520e+01\n2.54777963985013e-01\t1.39999999999998e+00 9.74974823996184e-01 -2.28897312885408e-13 2.70772471414849e+00\t4.87268266995840e+00 3.05235506015868e+00 8.00672160105256e-01 2.99629195973747e+01\n2.56116756064054e-01\t1.39999999999998e+00 9.69790154515562e-01 -2.18471879967360e-13 2.71202887962358e+00\t4.87500965171239e+00 3.03128814593556e+00 8.11275650753844e-01 2.99580713424564e+01\n2.57462749870346e-01\t1.39999999999998e+00 9.64560319718648e-01 -2.01663528936222e-13 2.71630144162040e+00\t4.87697562304686e+00 3.01027725712130e+00 8.22226429982092e-01 2.99496825773566e+01\n2.58815721775876e-01\t1.39999999999998e+00 9.59317803017231e-01 -1.88523521648240e-13 2.72050831421051e+00\t4.87872540547958e+00 2.98941655951431e+00 8.33421119847955e-01 2.99385519668942e+01\n2.60175452439700e-01\t1.39999999999998e+00 9.54068408882153e-01 -1.86608646214313e-13 2.72462229526088e+00\t4.88035237225722e+00 2.96870072600873e+00 8.44896877740239e-01 2.99251252055999e+01\n2.61542007232891e-01\t1.39999999999998e+00 9.48801343518493e-01 -1.96197696758921e-13 2.72866452064269e+00\t4.88187447273232e+00 2.94809150005210e+00 8.56609480803279e-01 2.99099113811300e+01\n2.62915513264442e-01\t1.39999999999998e+00 9.43505280101052e-01 -2.10739011356789e-13 2.73263011813186e+00\t4.88325476015837e+00 2.92761960022467e+00 8.68548431019766e-01 2.98924063901523e+01\n2.64296043042365e-01\t1.39999999999998e+00 9.38203017005886e-01 -2.23233390145976e-13 2.73650334153353e+00\t4.88456654786637e+00 2.90732437053360e+00 8.80758501567410e-01 2.98730124940592e+01\n2.65683692362728e-01\t1.39999999999998e+00 9.32878103497001e-01 -2.31054299006383e-13 2.74031301544077e+00\t4.88571833740319e+00 2.88715053768890e+00 8.93340289444000e-01 2.98513295761028e+01\n2.67079492902546e-01\t1.39999999999998e+00 9.27528350429381e-01 -2.38911891304222e-13 2.74407595090793e+00\t4.88665369907202e+00 2.86704392374062e+00 9.06320903163869e-01 2.98271980955098e+01\n2.68485202530698e-01\t1.39999999999998e+00 9.22137741355100e-01 -2.39300735740996e-13 2.74777706936923e+00\t4.88745833525258e+00 2.84703289678461e+00 9.19461619838976e-01 2.98010540711024e+01\n2.69903211794963e-01\t1.39999999999998e+00 9.16705670936021e-01 -2.31817314504959e-13 2.75142666993340e+00\t4.88808599399614e+00 2.82710937088260e+00 9.32575080339939e-01 2.97723366487251e+01\n2.71335838468341e-01\t1.39999999999998e+00 9.11208765115290e-01 -2.15698612097535e-13 2.75504817000441e+00\t4.88856857321099e+00 2.80720429634607e+00 9.45509982743062e-01 2.97414465230356e+01\n2.72786998256923e-01\t1.39999999999998e+00 9.05615923661696e-01 -2.00951870250050e-13 2.75867127353729e+00\t4.88898433980216e+00 2.78723727148395e+00 9.58105182129763e-01 2.97092896470746e+01\n2.74264056689625e-01\t1.39999999999998e+00 8.99906500160770e-01 -1.84077489031536e-13 2.76234615308025e+00\t4.88931147733000e+00 2.76710807315538e+00 9.70005610927303e-01 2.96757828619146e+01\n2.75747782571134e-01\t1.39999999999998e+00 8.94186547476221e-01 -1.72940397427316e-13 2.76604090607620e+00\t4.88959127934051e+00 2.74714231985293e+00 9.80957000890844e-01 2.96416325704069e+01\n2.77219261169939e-01\t1.39999999999998e+00 8.88511340101166e-01 -1.71054868743334e-13 2.76974474442532e+00\t4.88979403243807e+00 2.72756227960522e+00 9.90854346345020e-01 2.96071963293202e+01\n2.78659692350163e-01\t1.39999999999998e+00 8.82957744650694e-01 -1.71619793679780e-13 2.77339667165973e+00\t4.89001466458508e+00 2.70866443912806e+00 9.99547505501813e-01 2.95734364134452e+01\n2.80078847555015e-01\t1.39999999999998e+00 8.77513809245620e-01 -1.68105520373837e-13 2.77704365936494e+00\t4.89026156570699e+00 2.69032311090233e+00 1.00697134162028e+00 2.95401387647575e+01\n2.81476538746032e-01\t1.39999999999998e+00 8.72150386383348e-01 -1.62096773322549e-13 2.78069009542292e+00\t4.89053019373884e+00 2.67254143404263e+00 1.01303560072565e+00 2.95073031075289e+01\n2.82843711021569e-01\t1.39999999999998e+00 8.66881726794154e-01 -1.59184108390483e-13 2.78429098921699e+00\t4.89094034220934e+00 2.65541365469380e+00 1.01758984531473e+00 2.94760209160953e+01\n2.84193471948875e-01\t1.39999999999998e+00 8.61640087472892e-01 -1.62118783385008e-13 2.78788333792018e+00\t4.89156862036063e+00 2.63864349471251e+00 1.02052873311970e+00 2.94466599462887e+01\n2.85519842746975e-01\t1.39999999999998e+00 8.56474692310269e-01 -1.57577373830981e-13 2.79147647725331e+00\t4.89233238454880e+00 2.62230854233298e+00 1.02180543333337e+00 2.94191566078096e+01\n2.86818505403306e-01\t1.39999999999998e+00 8.51438134636713e-01 -1.50585510656527e-13 2.79504784103215e+00\t4.89320897542820e+00 2.60651574860201e+00 1.02155629549694e+00 2.93933811030416e+01\n2.88102752678613e-01\t1.39999999999998e+00 8.46502710035021e-01 -1.50790937906144e-13 2.79861949117344e+00\t4.89421366917090e+00 2.59109432639579e+00 1.01990713551998e+00 2.93689436457942e+01\n2.89365713635941e-01\t1.39999999999998e+00 8.41705239980945e-01 -1.49118173159265e-13 2.80214641327339e+00\t4.89535294506039e+00 2.57616392498497e+00 1.01702650029214e+00 2.93458947085395e+01\n2.90597042957342e-01\t1.39999999999998e+00 8.37099229174808e-01 -1.51884104341604e-13 2.80561136777152e+00\t4.89650387667598e+00 2.56184416083300e+00 1.01311247891639e+00 2.93237466185568e+01\n2.91813291322433e-01\t1.39999999999998e+00 8.32623154364570e-01 -1.53490838901106e-13 2.80905642833357e+00\t4.89768273910105e+00 2.54783241321072e+00 1.00824970920166e+00 2.93020857749800e+01\n2.93007003478967e-01\t1.39999999999998e+00 8.28288878708775e-01 -1.56667624582679e-13 2.81246606275961e+00\t4.89887932106156e+00 2.53420195235045e+00 1.00255558670935e+00 2.92809683303570e+01\n2.94174154973175e-01\t1.39999999999998e+00 8.24108364868846e-01 -1.50431440219315e-13 2.81579668182862e+00\t4.90012293422880e+00 2.52100903906109e+00 9.96246639558555e-01 2.92607037692876e+01\n2.95325371543405e-01\t1.39999999999998e+00 8.20041120978636e-01 -1.38927514240780e-13 2.81905024960302e+00\t4.90138422813843e+00 2.50810696436930e+00 9.89466731225774e-01 2.92407649740647e+01\n2.96468119987663e-01\t1.39999999999998e+00 8.16046046376547e-01 -1.34364094624294e-13 2.82225230585820e+00\t4.90259788896930e+00 2.49541760847933e+00 9.82281144103695e-01 2.92207623494492e+01\n2.97616158118358e-01\t1.39999999999998e+00 8.12076567785424e-01 -1.27298864574977e-13 2.82544198126695e+00\t4.90372922834466e+00 2.48270841758809e+00 9.74665587100757e-01 2.92001654320859e+01\n2.98776107246170e-01\t1.39999999999998e+00 8.08098370300659e-01 -1.25046501516680e-13 2.82864706031707e+00\t4.90471441772423e+00 2.46979282712347e+00 9.66648766006306e-01 2.91784282747204e+01\n2.99949683361907e-01\t1.39999999999998e+00 8.04096397928928e-01 -1.23755244519089e-13 2.83187324286768e+00\t4.90552339081502e+00 2.45658475354785e+00 9.58314280921332e-01 2.91554051028647e+01\n3.01141248200583e-01\t1.39999999999998e+00 8.00048053897579e-01 -1.21495544773305e-13 2.83512103285562e+00\t4.90615833048018e+00 2.44309227232778e+00 9.49709781681729e-01 2.91310633605882e+01\n3.02354314363510e-01\t1.39999999999998e+00 7.95945504065100e-01 -1.07812622611336e-13 2.83839201373630e+00\t4.90659754694798e+00 2.42939077344230e+00 9.40881748241797e-01 2.91053438396044e+01\n3.03580428248640e-01\t1.39999999999998e+00 7.91830471103430e-01 -1.01649805122835e-13 2.84163810821780e+00\t4.90686299231486e+00 2.41561459564477e+00 9.31973754966417e-01 2.90787407649946e+01\n3.04794441319209e-01\t1.39999999999998e+00 7.87788710479397e-01 -1.04584480117359e-13 2.84480258364541e+00\t4.90692681026050e+00 2.40200495308908e+00 9.23182441987164e-01 2.90519045169077e+01\n3.06006469146397e-01\t1.39999999999998e+00 7.83799434273584e-01 -9.32676396697248e-14 2.84790843579170e+00\t4.90678445827079e+00 2.38847925318608e+00 9.14455755528925e-01 2.90246033216375e+01\n3.07226228146268e-01\t1.39999999999998e+00 7.79822158756125e-01 -9.29668354827861e-14 2.85098771967008e+00\t4.90643514426170e+00 2.37490611338432e+00 9.05757626382399e-01 2.89964615808833e+01\n3.08464483294701e-01\t1.39999999999998e+00 7.75806955799603e-01 -9.78860844423574e-14 2.85406337765825e+00\t4.90588188470913e+00 2.36118172888489e+00 8.97070274314798e-01 2.89672731134382e+01\n3.09708278079316e-01\t1.39999999999998e+00 7.71776988041485e-01 -9.91663364087187e-14 2.85710304402535e+00\t4.90510507180826e+00 2.34745701836743e+00 8.88557533761759e-01 2.89372382121374e+01\n3.10940024805408e-01\t1.39999999999998e+00 7.67789174025971e-01 -9.94414621894553e-14 2.86005523884261e+00\t4.90425931250613e+00 2.33397633018236e+00 8.80395717733594e-01 2.89076603871314e+01\n3.12161570436881e-01\t1.39999999999998e+00 7.63826709079625e-01 -9.47606555731891e-14 2.86292847038144e+00\t4.90332609005862e+00 2.32069528750819e+00 8.72592526252588e-01 2.88783922497263e+01\n3.13379761410731e-01\t1.39999999999998e+00 7.59861989817660e-01 -9.88178437531189e-14 2.86575820089209e+00\t4.90220279485403e+00 2.30749325616284e+00 8.65021498887168e-01 2.88488674714555e+01\n3.14590980185537e-01\t1.39999999999998e+00 7.55922108120527e-01 -1.12229308478095e-13 2.86853625531115e+00\t4.90094471582044e+00 2.29450324582659e+00 8.57648864992411e-01 2.88191151780901e+01\n3.15786138335382e-01\t1.39999999999998e+00 7.52043825572807e-01 -1.07863979423740e-13 2.87122482982763e+00\t4.89960725250281e+00 2.28191262750462e+00 8.50506996718844e-01 2.87894496378395e+01\n3.16972768790600e-01\t1.39999999999998e+00 7.48201848682011e-01 -1.06752471269564e-13 2.87382589834149e+00\t4.89817306602188e+00 2.26962571402860e+00 8.43855330881394e-01 2.87596203761656e+01\n3.18160437182427e-01\t1.39999999999998e+00 7.44326439543361e-01 -1.01712166966468e-13 2.87637576410201e+00\t4.89658548258287e+00 2.25741141940152e+00 8.37571407593881e-01 2.87289601328947e+01\n3.19356237977560e-01\t1.39999999999998e+00 7.40382147478828e-01 -8.29045685953110e-14 2.87887363874388e+00\t4.89489330905938e+00 2.24513210134409e+00 8.31705911826424e-01 2.86976731573541e+01\n3.20566177503362e-01\t1.39999999999998e+00 7.36349550711572e-01 -7.33118497069597e-14 2.88134174513251e+00\t4.89314303783840e+00 2.23269590856198e+00 8.26093421982953e-01 2.86658869478657e+01\n3.21791992953619e-01\t1.39999999999998e+00 7.32233351856613e-01 -7.22700400839036e-14 2.88379449255992e+00\t4.89135363171636e+00 2.22008909680351e+00 8.20632973175167e-01 2.86336592409785e+01\n3.23031647163946e-01\t1.39999999999998e+00 7.28039532528978e-01 -6.79964196231276e-14 2.88622985364667e+00\t4.88953847498116e+00 2.20743223099140e+00 8.15301167375971e-01 2.86010211084705e+01\n3.24288226983006e-01\t1.39999999999998e+00 7.23754173686125e-01 -6.09972197611872e-14 2.88864797965593e+00\t4.88770376592588e+00 2.19476701516015e+00 8.10105796113228e-01 2.85678388274819e+01\n3.25566459286628e-01\t1.39999999999998e+00 7.19357783145899e-01 -5.82863137349954e-14 2.89107062393946e+00\t4.88581546383150e+00 2.18200712722618e+00 8.04991317990083e-01 2.85338718743663e+01\n3.26864287052102e-01\t1.39999999999998e+00 7.14867928265996e-01 -5.01719373751358e-14 2.89350893565016e+00\t4.88373349107255e+00 2.16919961229427e+00 7.99878074038862e-01 2.84983326642974e+01\n3.28173061228861e-01\t1.39999999999998e+00 7.10300812325397e-01 -4.75894233799544e-14 2.89595471174848e+00\t4.88141659563749e+00 2.15636892787174e+00 7.94759236455712e-01 2.84612206169014e+01\n3.29490134500646e-01\t1.39999999999998e+00 7.05614411137682e-01 -5.55313875838857e-14 2.89836700449595e+00\t4.87912148874272e+00 2.14333009613393e+00 7.89709264406414e-01 2.84238873457646e+01\n3.30799927510112e-01\t1.39999999999998e+00 7.00877596852338e-01 -5.62283728950852e-14 2.90071758786782e+00\t4.87687276106919e+00 2.13036679643667e+00 7.84833656985077e-01 2.83868392864874e+01\n3.32102346219420e-01\t1.39999999999998e+00 6.96100410190118e-01 -7.37227042061931e-14 2.90301217754447e+00\t4.87468967803031e+00 2.11761062737827e+00 7.80190199520112e-01 2.83503320279643e+01\n3.33392100926239e-01\t1.39999999999998e+00 6.91323888122760e-01 -9.29594987952998e-14 2.90524441902216e+00\t4.87262303516355e+00 2.10509822656818e+00 7.75840303256212e-01 2.83147417607769e+01\n3.34667667278607e-01\t1.39999999999998e+00 6.86568468265620e-01 -1.06293928301669e-13 2.90741232434129e+00\t4.87064242109073e+00 2.09280380831065e+00 7.71707675705297e-01 2.82799476247519e+01\n3.35931110822419e-01\t1.39999999999998e+00 6.81790311414094e-01 -1.15152978441390e-13 2.90953730937377e+00\t4.86864476289640e+00 2.08065468386644e+00 7.67678548863073e-01 2.82452723935662e+01\n3.37188115145127e-01\t1.39999999999998e+00 6.76913136921676e-01 -1.14830164191992e-13 2.91164610241120e+00\t4.86662324495616e+00 2.06843175326118e+00 7.63683093885896e-01 2.82103588740690e+01\n3.38444708221921e-01\t1.39999999999998e+00 6.71943536290581e-01 -1.27302532918720e-13 2.91373752692351e+00\t4.86462730225640e+00 2.05622971499309e+00 7.59728403181150e-01 2.81755794528438e+01\n3.39699584853677e-01\t1.39999999999998e+00 6.66928032116633e-01 -1.34375099655524e-13 2.91580468260552e+00\t4.86266808790731e+00 2.04415550392691e+00 7.55859770277607e-01 2.81413091855281e+01\n3.40942064937132e-01\t1.39999999999998e+00 6.61923884921002e-01 -1.36308316808167e-13 2.91782410010796e+00\t4.86076538799431e+00 2.03230975332205e+00 7.52130150379121e-01 2.81079605697460e+01\n3.42177925001240e-01\t1.39999999999998e+00 6.56911917891204e-01 -1.45530532978459e-13 2.91980520099770e+00\t4.85891976800048e+00 2.02061205630495e+00 7.48493960699342e-01 2.80755984882984e+01\n3.43413464667358e-01\t1.39999999999998e+00 6.51826776710637e-01 -1.53395461963784e-13 2.92177017004338e+00\t4.85706775151098e+00 2.00894597647696e+00 7.44916062932119e-01 2.80438654155823e+01\n3.44654515719600e-01\t1.39999999999998e+00 6.46643966816566e-01 -1.51964807903954e-13 2.92373525631135e+00\t4.85511532746308e+00 1.99732689263933e+00 7.41373383420629e-01 2.80120923057266e+01\n3.45904987662025e-01\t1.39999999999998e+00 6.41330036317402e-01 -1.52203250247259e-13 2.92570674347448e+00\t4.85309825727347e+00 1.98577982356291e+00 7.37881951888879e-01 2.79808059154679e+01\n3.47165317982705e-01\t1.39999999999998e+00 6.35889477768307e-01 -1.56264106770932e-13 2.92768059230996e+00\t4.85101578247416e+00 1.97419246315737e+00 7.34439221335003e-01 2.79504281172142e+01\n3.48434253757067e-01\t1.39999999999998e+00 6.30352874585615e-01 -1.61076973761952e-13 2.92964348479525e+00\t4.84888927859043e+00 1.96251866415749e+00 7.31014376559116e-01 2.79212179664038e+01\n3.49712311531312e-01\t1.39999999999998e+00 6.24707350768944e-01 -1.71377682992732e-13 2.93160618750757e+00\t4.84669252574620e+00 1.95083050595787e+00 7.27569895571791e-01 2.78931081945552e+01\n3.51000274219779e-01\t1.39999999999998e+00 6.18913626964202e-01 -1.83464875626429e-13 2.93356867182134e+00\t4.84443351546134e+00 1.93923520778455e+00 7.24157468340603e-01 2.78661816744827e+01\n3.52297983161378e-01\t1.39999999999998e+00 6.12983115377672e-01 -1.92529353015766e-13 2.93553899117493e+00\t4.84206520223086e+00 1.92761889963432e+00 7.20806590461770e-01 2.78400813045171e+01\n3.53604081918850e-01\t1.39999999999998e+00 6.06943520911632e-01 -1.91958925563705e-13 2.93751357660156e+00\t4.83961070631952e+00 1.91589569177662e+00 7.17499698499666e-01 2.78147626826946e+01\n3.54904891356312e-01\t1.39999999999998e+00 6.00842803928010e-01 -1.84369122359116e-13 2.93944647710508e+00\t4.83726637010398e+00 1.90419356687432e+00 7.14267558373624e-01 2.77909404167207e+01\n3.56195779753563e-01\t1.39999999999998e+00 5.94730548929557e-01 -1.83398845439052e-13 2.94133344174503e+00\t4.83498391339494e+00 1.89283280937101e+00 7.11154046086613e-01 2.77683527909259e+01\n3.57475191593844e-01\t1.39999999999998e+00 5.88584391238944e-01 -1.87223093791291e-13 2.94317889348357e+00\t4.83275329645967e+00 1.88172267663462e+00 7.08149857501785e-01 2.77471070526689e+01\n3.58746787445597e-01\t1.39999999999998e+00 5.82389387816777e-01 -1.83464875626429e-13 2.94498491174228e+00\t4.83056066466674e+00 1.87069484023026e+00 7.05224421541675e-01 2.77271081821080e+01\n3.60010527750963e-01\t1.39999999999998e+00 5.76147958865209e-01 -1.86276661105557e-13 2.94674631760165e+00\t4.82836146702131e+00 1.85966283001084e+00 7.02362457080455e-01 2.77081584327852e+01\n3.61264064940365e-01\t1.39999999999998e+00 5.69878760449151e-01 -1.87426686869036e-13 2.94845765649961e+00\t4.82614330818338e+00 1.84890480166350e+00 6.99601320861846e-01 2.76902168018012e+01\n3.62505164025845e-01\t1.39999999999998e+00 5.63587732705594e-01 -1.87714651852874e-13 2.95012217866447e+00\t4.82392019013124e+00 1.83849404769176e+00 6.96969711933503e-01 2.76732425891619e+01\n3.63739695862362e-01\t1.39999999999998e+00 5.57246381908041e-01 -1.90788723909638e-13 2.95175348386313e+00\t4.82168902000052e+00 1.82820746879274e+00 6.94436986167674e-01 2.76570794828826e+01\n3.64973856209499e-01\t1.39999999999998e+00 5.50833228576484e-01 -1.89603848880599e-13 2.95335809612565e+00\t4.81948659017901e+00 1.81793522387269e+00 6.91981580979168e-01 2.76418062875672e+01\n3.66197039315942e-01\t1.39999999999997e+00 5.44378061781533e-01 -1.87644953321754e-13 2.95493137267359e+00\t4.81733033407514e+00 1.80770813634007e+00 6.89574810427543e-01 2.76275690747797e+01\n3.67409300577156e-01\t1.39999999999998e+00 5.37888151422623e-01 -1.81696733942228e-13 2.95647179090199e+00\t4.81520572182901e+00 1.79772250766637e+00 6.87238023135224e-01 2.76142679605733e+01\n3.68614307038339e-01\t1.39999999999998e+00 5.31335638469424e-01 -1.86900279541894e-13 2.95797206297608e+00\t4.81310397952635e+00 1.78792056414123e+00 6.85005052666414e-01 2.76018835247765e+01\n3.69820194173685e-01\t1.39999999999998e+00 5.24719942019624e-01 -2.01392071499229e-13 2.95944234287003e+00\t4.81099751347172e+00 1.77830768773989e+00 6.82897183918011e-01 2.75901975533809e+01\n3.71016779569947e-01\t1.39999999999998e+00 5.18101804098051e-01 -2.30377489585771e-13 2.96087533730153e+00\t4.80889853518052e+00 1.76891530317529e+00 6.80902182267038e-01 2.75793545568043e+01\n3.72204700379110e-01\t1.39999999999997e+00 5.11425579413478e-01 -2.46902461062750e-13 2.96227950411000e+00\t4.80679370004393e+00 1.75973396791284e+00 6.78965614366605e-01 2.75692718007839e+01\n3.73385217959533e-01\t1.39999999999997e+00 5.04706819273451e-01 -2.63307294282141e-13 2.96365495073475e+00\t4.80466786992191e+00 1.75072082281183e+00 6.77127934058747e-01 2.75599372640754e+01\n3.74560624739364e-01\t1.39999999999998e+00 4.97952078376445e-01 -2.76119901891047e-13 2.96500094224001e+00\t4.80255857454676e+00 1.74187851886427e+00 6.75412935474071e-01 2.75516719359082e+01\n3.75725649508640e-01\t1.39999999999997e+00 4.91176450530304e-01 -2.84790032327994e-13 2.96631369499579e+00\t4.80047669654082e+00 1.73318686894929e+00 6.73797568930145e-01 2.75445923689238e+01\n3.76884144383248e-01\t1.39999999999998e+00 4.84348250739638e-01 -2.98011660264262e-13 2.96760550791520e+00\t4.79841872960553e+00 1.72467649150991e+00 6.72267836368348e-01 2.75385108108670e+01\n3.78036044264207e-01\t1.39999999999997e+00 4.77479633986048e-01 -3.25099627549657e-13 2.96887176763834e+00\t4.79640204250788e+00 1.71636836965878e+00 6.70786534006362e-01 2.75333363644150e+01\n3.79189429015790e-01\t1.39999999999997e+00 4.70534118189908e-01 -3.52531502060973e-13 2.97011130161413e+00\t4.79445665483511e+00 1.70822097890844e+00 6.69368985117845e-01 2.75290194501331e+01\n3.80331990475494e-01\t1.39999999999997e+00 4.63581907690693e-01 -3.65711861130130e-13 2.97131670027718e+00\t4.79261110795377e+00 1.70033469786952e+00 6.68023799147519e-01 2.75255694943252e+01\n3.81467814888531e-01\t1.39999999999997e+00 4.56575649775021e-01 -3.75987809040644e-13 2.97250086102366e+00\t4.79079749103907e+00 1.69262898081263e+00 6.66704981032230e-01 2.75224993929866e+01\n3.82596341674201e-01\t1.39999999999998e+00 4.49508398901469e-01 -3.79718514627433e-13 2.97366266115607e+00\t4.78897535609182e+00 1.68503889203690e+00 6.65423289713222e-01 2.75194386425939e+01\n3.83726306313971e-01\t1.39999999999997e+00 4.42337387348920e-01 -3.67762465282554e-13 2.97480929302183e+00\t4.78704088329679e+00 1.67750017069735e+00 6.64231331990676e-01 2.75159165589525e+01\n3.84853677244190e-01\t1.39999999999997e+00 4.35108370943391e-01 -3.99332231536149e-13 2.97592589694841e+00\t4.78507515768115e+00 1.67011655177580e+00 6.63147597690291e-01 2.75124818503580e+01\n3.85974373462214e-01\t1.39999999999998e+00 4.27849386775344e-01 -4.26444043055874e-13 2.97701623593190e+00\t4.78309141862537e+00 1.66297589788770e+00 6.62150732208997e-01 2.75091086885183e+01\n3.87085938121507e-01\t1.39999999999998e+00 4.20582370890602e-01 -4.42363737815232e-13 2.97807365836130e+00\t4.78107450024227e+00 1.65604695418296e+00 6.61254045802912e-01 2.75056635864372e+01\n3.88197267919026e-01\t1.39999999999998e+00 4.13242781467192e-01 -4.48799846912598e-13 2.97910644068829e+00\t4.77896519506127e+00 1.64929036350837e+00 6.60444812163416e-01 2.75020151165811e+01\n3.89311935723786e-01\t1.39999999999997e+00 4.05836239732614e-01 -4.60092843125902e-13 2.98010748047159e+00\t4.77681454910878e+00 1.64279026747799e+00 6.59739094831340e-01 2.74983733257281e+01\n3.90401170244592e-01\t1.39999999999997e+00 3.98544104186714e-01 -4.64186714743264e-13 2.98104798618998e+00\t4.77471062668993e+00 1.63674796422242e+00 6.59147829490560e-01 2.74949865965496e+01\n3.91465714535107e-01\t1.39999999999997e+00 3.91378696936630e-01 -4.57071044967478e-13 2.98192987652011e+00\t4.77263666743462e+00 1.63118802325629e+00 6.58676165657119e-01 2.74917448302617e+01\n3.92513172094961e-01\t1.39999999999997e+00 3.84279873817636e-01 -4.33693607378285e-13 2.98277463883897e+00\t4.77051888945552e+00 1.62599071157539e+00 6.58333970192044e-01 2.74882796161262e+01\n3.93543371186415e-01\t1.39999999999997e+00 3.77267078009418e-01 -4.28872486613843e-13 2.98357773480145e+00\t4.76837332362662e+00 1.62110383207115e+00 6.58112289540254e-01 2.74846149070657e+01\n3.94562412032971e-01\t1.39999999999997e+00 3.70277383593917e-01 -4.37560041683570e-13 2.98434674649687e+00\t4.76620006521798e+00 1.61652021227340e+00 6.57975685267710e-01 2.74806745082221e+01\n3.95568497082272e-01\t1.39999999999997e+00 3.63325202673390e-01 -4.47146340970371e-13 2.98508413061604e+00\t4.76400416432429e+00 1.61228286257972e+00 6.57906611076114e-01 2.74764019959401e+01\n3.96562748004463e-01\t1.39999999999997e+00 3.56413015481476e-01 -4.48356894405612e-13 2.98579058511698e+00\t4.76177642504539e+00 1.60828388875315e+00 6.57911553538683e-01 2.74718242320704e+01\n3.97534685801829e-01\t1.39999999999997e+00 3.49619024288144e-01 -4.40055432514852e-13 2.98645484021139e+00\t4.75955082684779e+00 1.60458723869107e+00 6.58039791703803e-01 2.74670943243435e+01\n3.98493287233004e-01\t1.39999999999997e+00 3.42859131066236e-01 -4.45993563949085e-13 2.98708632097207e+00\t4.75730654021352e+00 1.60117216572838e+00 6.58252536386216e-01 2.74621001617106e+01\n3.99446302951460e-01\t1.39999999999997e+00 3.36102171491970e-01 -4.41831827972475e-13 2.98768732180763e+00\t4.75502791826765e+00 1.59803426790476e+00 6.58536176012735e-01 2.74568854314135e+01\n4.00396075408818e-01\t1.39999999999997e+00 3.29322868017173e-01 -4.36727327653874e-13 2.98826718800402e+00\t4.75269375060725e+00 1.59513634809543e+00 6.58930092466310e-01 2.74513502176884e+01\n4.01340000272932e-01\t1.39999999999997e+00 3.22500387501295e-01 -4.18095809782388e-13 2.98883043125648e+00\t4.75030847841621e+00 1.59245142147793e+00 6.59419127373958e-01 2.74455154957477e+01\n4.02284863657843e-01\t1.39999999999997e+00 3.15599760599242e-01 -4.18482820047291e-13 2.98937961961830e+00\t4.74785005797635e+00 1.58995767526229e+00 6.59949390190646e-01 2.74393252412088e+01\n4.03228508975463e-01\t1.39999999999997e+00 3.08670139400269e-01 -4.17488698892896e-13 2.98990963713189e+00\t4.74533978011137e+00 1.58776083438150e+00 6.60574770536741e-01 2.74329383495093e+01\n4.04177324130718e-01\t1.39999999999997e+00 3.01635232967391e-01 -4.18668071406320e-13 2.99042664276840e+00\t4.74276604341885e+00 1.58582237885580e+00 6.61286572158845e-01 2.74262983195235e+01\n4.05117765186730e-01\t1.39999999999997e+00 2.94613698130289e-01 -4.26549507938490e-13 2.99091656734603e+00\t4.74018453827603e+00 1.58418612586327e+00 6.62069826451108e-01 2.74196148397569e+01\n4.06053955551748e-01\t1.39999999999997e+00 2.87580208287522e-01 -4.37246398293531e-13 2.99137733519675e+00\t4.73758044617592e+00 1.58283266980390e+00 6.62917950030886e-01 2.74128646538599e+01\n4.06989184544076e-01\t1.39999999999997e+00 2.80492489912501e-01 -4.56096182617734e-13 2.99181630530967e+00\t4.73492618577081e+00 1.58177932001250e+00 6.63849787356284e-01 2.74058240436092e+01\n4.07919701522092e-01\t1.39999999999997e+00 2.73379547421392e-01 -4.58326535613573e-13 2.99223400796441e+00\t4.73224439970673e+00 1.58101712133628e+00 6.64868134008200e-01 2.73985776731145e+01\n4.08849039944787e-01\t1.39999999999997e+00 2.66253782501912e-01 -4.60184551719481e-13 2.99262678571697e+00\t4.72954669362778e+00 1.58057519245399e+00 6.65942211192536e-01 2.73913601554627e+01\n4.09766333867762e-01\t1.39999999999997e+00 2.59168669617034e-01 -4.58161460145131e-13 2.99300054378615e+00\t4.72683224372569e+00 1.58038241047615e+00 6.67051057555157e-01 2.73840744006469e+01\n4.10675294867225e-01\t1.39999999999997e+00 2.52076833745357e-01 -4.49236379818034e-13 2.99336949876730e+00\t4.72406334250061e+00 1.58039663411523e+00 6.68241343169770e-01 2.73764214961308e+01\n4.11583538381117e-01\t1.39999999999997e+00 2.44948341991045e-01 -4.42101451237597e-13 2.99372452066940e+00\t4.72120785846998e+00 1.58063558406979e+00 6.69476150698370e-01 2.73682856122888e+01\n4.12483729339670e-01\t1.39999999999997e+00 2.37849045890580e-01 -4.39430896992580e-13 2.99406110236015e+00\t4.71830619651210e+00 1.58115036373949e+00 6.70777864580708e-01 2.73598195670728e+01\n4.13371817624940e-01\t1.39999999999997e+00 2.30783681820869e-01 -4.33139687473068e-13 2.99437462822648e+00\t4.71538934510572e+00 1.58195053698570e+00 6.72134500138717e-01 2.73512618672393e+01\n4.14255720107356e-01\t1.39999999999997e+00 2.23700057223436e-01 -4.26848477953557e-13 2.99466510399697e+00\t4.71243283885179e+00 1.58312563628570e+00 6.73542850937988e-01 2.73427190145757e+01\n4.15142436961035e-01\t1.39999999999997e+00 2.16533993368823e-01 -4.18759779999899e-13 2.99494250089022e+00\t4.70939765297079e+00 1.58459178795636e+00 6.75025721215466e-01 2.73340613024141e+01\n4.16014670436360e-01\t1.39999999999997e+00 2.09442548113070e-01 -4.14163345289726e-13 2.99519702587690e+00\t4.70637065431140e+00 1.58629124476457e+00 6.76558427801809e-01 2.73256615378208e+01\n4.16878341124104e-01\t1.39999999999997e+00 2.02360491293788e-01 -4.06933039771966e-13 2.99543537980726e+00\t4.70335223154107e+00 1.58833860204103e+00 6.78164523718963e-01 2.73175143724885e+01\n4.17741164106221e-01\t1.39999999999997e+00 1.95226064383812e-01 -3.99607357316885e-13 2.99566315784464e+00\t4.70032077700957e+00 1.59071444923714e+00 6.79857686975201e-01 2.73094880766577e+01\n4.18603682917134e-01\t1.39999999999997e+00 1.88032695984681e-01 -3.90799663989569e-13 2.99587803416669e+00\t4.69728916438176e+00 1.59337751363355e+00 6.81642353306846e-01 2.73016227066312e+01\n4.19453140055196e-01\t1.39999999999997e+00 1.80903229599713e-01 -3.83099810472686e-13 2.99607155292421e+00\t4.69430578833998e+00 1.59629591276911e+00 6.83470639564025e-01 2.72940777038057e+01\n4.20297262746796e-01\t1.39999999999997e+00 1.73779275401206e-01 -3.71328095400901e-13 2.99624569866750e+00\t4.69132918253745e+00 1.59953039866615e+00 6.85366228292277e-01 2.72866585791001e+01\n4.21143280773737e-01\t1.39999999999997e+00 1.66584176248331e-01 -3.60286380734003e-13 2.99640502222130e+00\t4.68832108362582e+00 1.60306413346564e+00 6.87327770721095e-01 2.72792048261119e+01\n4.21983304821838e-01\t1.39999999999997e+00 1.59365950736095e-01 -3.54956277275198e-13 2.99655009258293e+00\t4.68531254278743e+00 1.60683061584558e+00 6.89315967796878e-01 2.72718289998870e+01\n4.22813372615972e-01\t1.39999999999997e+00 1.52185341904339e-01 -3.46387026291187e-13 2.99668065189013e+00\t4.68232535275911e+00 1.61087437287177e+00 6.91330751177194e-01 2.72646202398895e+01\n4.23641069258658e-01\t1.39999999999997e+00 1.45001224999227e-01 -3.22473093429558e-13 2.99679902465747e+00\t4.67933478589392e+00 1.61525442547756e+00 6.93408182951080e-01 2.72575273147547e+01\n4.24469911839580e-01\t1.39999999999997e+00 1.37764684309406e-01 -3.06038913460222e-13 2.99690877606501e+00\t4.67634377615145e+00 1.61987722557798e+00 6.95518355474385e-01 2.72505702236719e+01\n4.25285584251405e-01\t1.39999999999997e+00 1.30593602745989e-01 -2.95305339667749e-13 2.99701144909761e+00\t4.67339422235132e+00 1.62460656765507e+00 6.97629622908396e-01 2.72437550789171e+01\n4.26094762912978e-01\t1.39999999999997e+00 1.23429549247598e-01 -2.95136595855564e-13 2.99710789137134e+00\t4.67045953311062e+00 1.62953640001091e+00 6.99767646192195e-01 2.72369747627783e+01\n4.26904239179257e-01\t1.39999999999997e+00 1.16216765094867e-01 -2.87752219900592e-13 2.99719731232747e+00\t4.66751366039647e+00 1.63480941300545e+00 7.01943612086038e-01 2.72301406833224e+01\n4.27703032700962e-01\t1.39999999999997e+00 1.09055798488467e-01 -2.80459552539199e-13 2.99727738197106e+00\t4.66456738086369e+00 1.64021500718770e+00 7.04121542041191e-01 2.72230352421950e+01\n4.28491643393131e-01\t1.39999999999997e+00 1.01919036616056e-01 -2.76409701046756e-13 2.99735004871080e+00\t4.66157239108279e+00 1.64565919876630e+00 7.06286643652874e-01 2.72152863629608e+01\n4.29277075939596e-01\t1.39999999999997e+00 9.47508098624909e-02 -2.73541056239608e-13 2.99741198869744e+00\t4.65852415908594e+00 1.65129706565809e+00 7.08490129069637e-01 2.72069951232843e+01\n4.30060643094220e-01\t1.39999999999997e+00 8.75400282199969e-02 -2.67697384656762e-13 2.99746584897466e+00\t4.65547584936877e+00 1.65730603408054e+00 7.10775406915126e-01 2.71986068740568e+01\n4.30832043063017e-01\t1.39999999999997e+00 8.04129193936206e-02 -2.51398933405923e-13 2.99751596046666e+00\t4.65246745964638e+00 1.66355226723312e+00 7.13092642437079e-01 2.71902347545738e+01\n4.31596211778845e-01\t1.39999999999997e+00 7.33243145017470e-02 -2.32921485971649e-13 2.99756494126333e+00\t4.64947815822897e+00 1.66995691532895e+00 7.15402784435125e-01 2.71818316900206e+01\n4.32359669382544e-01\t1.39999999999997e+00 6.61828137723331e-02 -2.22433691209968e-13 2.99761557850173e+00\t4.64648666218227e+00 1.67657078765121e+00 7.17703814240414e-01 2.71732770598648e+01\n4.33114794520226e-01\t1.39999999999997e+00 5.90742796153196e-02 -2.26329472265199e-13 2.99766702957334e+00\t4.64351960547145e+00 1.68339210346262e+00 7.19971865448477e-01 2.71646236500318e+01\n4.33860011537967e-01\t1.39999999999997e+00 5.20546749358385e-02 -2.32785757253152e-13 2.99772317613825e+00\t4.64058333318125e+00 1.69039049500033e+00 7.22202538930327e-01 2.71558808461500e+01\n4.34600961299987e-01\t1.39999999999997e+00 4.50660251020396e-02 -2.29924449133491e-13 2.99778871026846e+00\t4.63766892465205e+00 1.69756698469723e+00 7.24373701930628e-01 2.71471302814859e+01\n4.35337900476882e-01\t1.39999999999997e+00 3.80841409758198e-02 -2.24414596831272e-13 2.99786347998702e+00\t4.63478469071187e+00 1.70478433293886e+00 7.26416045616273e-01 2.71384334594710e+01\n4.36064701783334e-01\t1.39999999999997e+00 3.11619709891328e-02 -2.22162233772974e-13 2.99794524854634e+00\t4.63198245144131e+00 1.71196889588956e+00 7.28333959184691e-01 2.71299270718413e+01\n4.36784634404839e-01\t1.39999999999997e+00 2.42906209446431e-02 -2.10966448668864e-13 2.99803155427837e+00\t4.62924167962029e+00 1.71929651230228e+00 7.30160045626078e-01 2.71213870351108e+01\n4.37502831491823e-01\t1.39999999999997e+00 1.74554368023981e-02 -1.91392166455387e-13 2.99812944290647e+00\t4.62653023680130e+00 1.72689753411795e+00 7.31929974883209e-01 2.71127158055291e+01\n4.38211365232311e-01\t1.39999999999997e+00 1.07358290987852e-02 -1.78406229604617e-13 2.99823855093184e+00\t4.62389380198736e+00 1.73453460523739e+00 7.33583352854551e-01 2.71041339076082e+01\n4.38912337795493e-01\t1.39999999999997e+00 4.04156511971752e-03 -1.77944018292980e-13 2.99835818592952e+00\t4.62133254493382e+00 1.74211897579023e+00 7.35077428682717e-01 2.70956729023839e+01\n4.39609059460476e-01\t1.39999999999997e+00 -2.69495097810975e-03 -1.81590351973676e-13 2.99849401404563e+00\t4.61883267863203e+00 1.74976346845112e+00 7.36457093385648e-01 2.70872970255572e+01\n4.40306260433826e-01\t1.39999999999997e+00 -9.43557100288136e-03 -1.94723022574172e-13 2.99864432574129e+00\t4.61637377950679e+00 1.75756092137671e+00 7.37737760420134e-01 2.70789166501386e+01\n4.41008399933780e-01\t1.39999999999997e+00 -1.62227396774676e-02 -2.09484437796630e-13 2.99880836356220e+00\t4.61393798043198e+00 1.76545525639310e+00 7.38887742794924e-01 2.70704602169748e+01\n4.41704967844731e-01\t1.39999999999997e+00 -2.29922355310080e-02 -2.13541625976559e-13 2.99898436693757e+00\t4.61156012746546e+00 1.77341720767071e+00 7.39880347606445e-01 2.70620483137530e+01\n4.42375250800209e-01\t1.39999999999997e+00 -2.95483688494564e-02 -2.10584940919576e-13 2.99916717639615e+00\t4.60930760004707e+00 1.78131428141836e+00 7.40758547513018e-01 2.70539415174865e+01\n4.43028368193503e-01\t1.39999999999997e+00 -3.59279079316491e-02 -2.17540120656599e-13 2.99935944548598e+00\t4.60713683914091e+00 1.78928667053042e+00 7.41570409939754e-01 2.70459987468238e+01\n4.43670480075123e-01\t1.39999999999997e+00 -4.21941511402005e-02 -2.10093382857993e-13 2.99956025868555e+00\t4.60502143920446e+00 1.79722626450130e+00 7.42316812504269e-01 2.70381006828943e+01\n4.44305688431512e-01\t1.39999999999997e+00 -4.84187485379377e-02 -1.92954880889972e-13 2.99977305993810e+00\t4.60293945797761e+00 1.80520664860137e+00 7.43021154853959e-01 2.70301332519884e+01\n4.44937044046354e-01\t1.39999999999997e+00 -5.46423983069514e-02 -1.76021806171566e-13 2.99999653014099e+00\t4.60087676854215e+00 1.81332101953747e+00 7.43681456601297e-01 2.70220052166924e+01\n4.45568445882107e-01\t1.39999999999997e+00 -6.08681222414277e-02 -1.69117983246948e-13 3.00022741577563e+00\t4.59881634082246e+00 1.82162961714200e+00 7.44309503021556e-01 2.70136389298879e+01\n4.46202873265855e-01\t1.39999999999997e+00 -6.71234052548365e-02 -1.69550847808640e-13 3.00046440486916e+00\t4.59673886775840e+00 1.83006041681458e+00 7.44862662060493e-01 2.70049731998061e+01\n4.46842202960184e-01\t1.39999999999997e+00 -7.34557146789169e-02 -1.71348336242787e-13 3.00070796576305e+00\t4.59462294719587e+00 1.83864597002537e+00 7.45330627091381e-01 2.69958837045738e+01\n4.47488483955262e-01\t1.39999999999997e+00 -7.99102644768346e-02 -1.69066626434544e-13 3.00095793839832e+00\t4.59246092423249e+00 1.84748371552094e+00 7.45737880965817e-01 2.69863261076286e+01\n4.48136153181383e-01\t1.39999999999997e+00 -8.63817919439354e-02 -1.66718886438924e-13 3.00120673142712e+00\t4.59027433355357e+00 1.85649503904876e+00 7.46114071975977e-01 2.69764327203041e+01\n4.48787571311795e-01\t1.39999999999997e+00 -9.28861340357629e-02 -1.65772453753190e-13 3.00145313288487e+00\t4.58804929558087e+00 1.86565491885253e+00 7.46463454330188e-01 2.69662166416011e+01\n4.49444370249277e-01\t1.39999999999997e+00 -9.94731867146436e-02 -1.65508333003683e-13 3.00169881978305e+00\t4.58575252933108e+00 1.87497649246108e+00 7.46784341277965e-01 2.69554046563111e+01\n4.50104062182681e-01\t1.39999999999997e+00 -1.06104720729159e-01 -1.65097478504450e-13 3.00193548345064e+00\t4.58339292214969e+00 1.88439173087060e+00 7.47121584438410e-01 2.69439165539645e+01\n4.50764676834834e-01\t1.39999999999997e+00 -1.12718343210485e-01 -1.65706423565814e-13 3.00215956626493e+00\t4.58096696610011e+00 1.89395387376379e+00 7.47506935644338e-01 2.69316573570232e+01\n4.51429558800302e-01\t1.39999999999997e+00 -1.19355999534627e-01 -1.65897177440458e-13 3.00237305010517e+00\t4.57844294418058e+00 1.90370208485614e+00 7.47947435196656e-01 2.69183302705539e+01\n4.52100188385238e-01\t1.39999999999997e+00 -1.26027636628781e-01 -1.64679287317730e-13 3.00257861284760e+00\t4.57576092674369e+00 1.91355442069991e+00 7.48442315933156e-01 2.69032628111091e+01\n4.52772253742324e-01\t1.39999999999997e+00 -1.32707901856747e-01 -1.66366725439582e-13 3.00276890635754e+00\t4.57295744182875e+00 1.92337662234716e+00 7.48981839950306e-01 2.68867543135407e+01\n4.53445432722408e-01\t1.39999999999997e+00 -1.39399131484946e-01 -1.64994764879641e-13 3.00293715632121e+00\t4.57009369165333e+00 1.93331760905959e+00 7.49584353285430e-01 2.68695268042935e+01\n4.54123104668015e-01\t1.39999999999997e+00 -1.46139037076502e-01 -1.70974165180985e-13 3.00308579627211e+00\t4.56713436767757e+00 1.94337011349380e+00 7.50236689975610e-01 2.68514565120025e+01\n4.54807154211705e-01\t1.39999999999997e+00 -1.52947669472179e-01 -1.81803115910779e-13 3.00321101724674e+00\t4.56407096810570e+00 1.95346097124411e+00 7.50914091990089e-01 2.68324073897098e+01\n4.55487894029122e-01\t1.39999999999997e+00 -1.59727024122813e-01 -1.92426639390957e-13 3.00330903116992e+00\t4.56094668836464e+00 1.96345115952740e+00 7.51610935443439e-01 2.68125413883167e+01\n4.56164068832808e-01\t1.39999999999997e+00 -1.66456819893789e-01 -2.13328862039456e-13 3.00338154790475e+00\t4.55776535417815e+00 1.97347238305990e+00 7.52346404401591e-01 2.67919733269998e+01\n4.56836474231302e-01\t1.39999999999997e+00 -1.73153531833918e-01 -2.28933996322839e-13 3.00343064010723e+00\t4.55451234449602e+00 1.98356226513226e+00 7.53123772117835e-01 2.67706510417121e+01\n4.57506036076825e-01\t1.39999999999997e+00 -1.79825270276336e-01 -2.47114307913917e-13 3.00346253289396e+00\t4.55114348222958e+00 1.99360337304507e+00 7.53933538488692e-01 2.67483169725401e+01\n4.58173758264959e-01\t1.39999999999997e+00 -1.86470090618575e-01 -2.57473710644588e-13 3.00346883304458e+00\t4.54768866581016e+00 2.00364903466090e+00 7.54799705377755e-01 2.67251924230704e+01\n4.58840331145425e-01\t1.39999999999997e+00 -1.93107231391354e-01 -2.69946079371316e-13 3.00344531141676e+00\t4.54417738967533e+00 2.01387608906502e+00 7.55748790450376e-01 2.67014278642080e+01\n4.59506685165804e-01\t1.39999999999997e+00 -1.99730259735691e-01 -2.66849997252093e-13 3.00339293169628e+00\t4.54061299240165e+00 2.02427788251371e+00 7.56786611028485e-01 2.66770381506818e+01\n4.60173871716785e-01\t1.39999999999997e+00 -2.06333019302054e-01 -2.55052603774105e-13 3.00331432385191e+00\t4.53697366419194e+00 2.03474521991014e+00 7.57896033253557e-01 2.66518293546427e+01\n4.60842893935275e-01\t1.39999999999997e+00 -2.12941741298950e-01 -2.47209684851239e-13 3.00321271778069e+00\t4.53323208471307e+00 2.04520879983980e+00 7.59083007310581e-01 2.66256114080314e+01\n4.61514650388622e-01\t1.39999999999997e+00 -2.19562815623539e-01 -2.40203148301812e-13 3.00308686705754e+00\t4.52938196045639e+00 2.05581241147099e+00 7.60379929590379e-01 2.65983834489713e+01\n4.62189817768480e-01\t1.39999999999997e+00 -2.26198757235011e-01 -2.29058720010107e-13 3.00293464000105e+00\t4.52543481241876e+00 2.06657780189351e+00 7.61773821266439e-01 2.65702797648449e+01\n4.62868833838766e-01\t1.39999999999997e+00 -2.32868418147746e-01 -2.14942933286445e-13 3.00276033443134e+00\t4.52139083934894e+00 2.07744170163299e+00 7.63242297035047e-01 2.65413206964240e+01\n4.63552368960808e-01\t1.39999999999997e+00 -2.39572936603640e-01 -2.01729559123599e-13 3.00256697186404e+00\t4.51725387210719e+00 2.08834626344105e+00 7.64770072110095e-01 2.65116025788979e+01\n4.64240788127356e-01\t1.39999999999997e+00 -2.46335815569193e-01 -2.01670865623709e-13 3.00235708289474e+00\t4.51304064667602e+00 2.09940692685901e+00 7.66323379036436e-01 2.64812967749763e+01\n4.64934427819107e-01\t1.39999999999997e+00 -2.53164196177232e-01 -2.10078709483021e-13 3.00213293974991e+00\t4.50875045108191e+00 2.11059189681000e+00 7.67884612223412e-01 2.64504513785533e+01\n4.65633095141666e-01\t1.39999999999997e+00 -2.60063009484207e-01 -2.19873187277246e-13 3.00189560687988e+00\t4.50435696617280e+00 2.12195114301144e+00 7.69453478783041e-01 2.64190112193733e+01\n4.66336525428369e-01\t1.39999999999997e+00 -2.67002812036570e-01 -2.05368556116809e-13 3.00164337851642e+00\t4.49986573562852e+00 2.13343980512404e+00 7.71024556474522e-01 2.63869164029903e+01\n4.67044944709577e-01\t1.39999999999997e+00 -2.73986785918764e-01 -1.93453775639041e-13 3.00137578573313e+00\t4.49532503998619e+00 2.14511490024414e+00 7.72584730824275e-01 2.63543830624218e+01\n4.67758353454210e-01\t1.39999999999997e+00 -2.81016206309337e-01 -1.85985027777976e-13 3.00109745077697e+00\t4.49073611491016e+00 2.15699197406599e+00 7.74151446353706e-01 2.63214609430508e+01\n4.68476360761065e-01\t1.39999999999997e+00 -2.88086956334877e-01 -1.75163413735668e-13 3.00081472372271e+00\t4.48608884754055e+00 2.16906583184543e+00 7.75705984050881e-01 2.62881377046600e+01\n4.69198273427592e-01\t1.39999999999997e+00 -2.95188649176407e-01 -1.62008733072713e-13 3.00052614884840e+00\t4.48139120156421e+00 2.18134599687268e+00 7.77266730384110e-01 2.62545133821426e+01\n4.69924790337391e-01\t1.39999999999997e+00 -3.02293449312848e-01 -1.54620688773998e-13 3.00023251957453e+00\t4.47666161897757e+00 2.19373274004326e+00 7.78845691928357e-01 2.62208282244017e+01\n4.70654394187002e-01\t1.39999999999997e+00 -3.09384119042038e-01 -1.46161488102282e-13 2.99993970181655e+00\t4.47190715669905e+00 2.20610230459033e+00 7.80413985148964e-01 2.61871849370875e+01\n4.71388016772048e-01\t1.39999999999997e+00 -3.16446716807428e-01 -1.39154951552855e-13 2.99964384974329e+00\t4.46713063501649e+00 2.21847587662808e+00 7.81958350939453e-01 2.61535317043178e+01\n4.72125145539430e-01\t1.39999999999997e+00 -3.23471914858989e-01 -1.24136752268377e-13 2.99935094609881e+00\t4.46233141763173e+00 2.23071821686864e+00 7.83461425387943e-01 2.61198865829001e+01\n4.72865579257469e-01\t1.39999999999997e+00 -3.30440144782838e-01 -1.10050312294661e-13 2.99906009261961e+00\t4.45752877713380e+00 2.24252724474996e+00 7.84925479266138e-01 2.60863674827755e+01\n4.73609976337325e-01\t1.39999999999997e+00 -3.37366153629395e-01 -1.08979155921659e-13 2.99877553115065e+00\t4.45272503129805e+00 2.25405625825727e+00 7.86346751365626e-01 2.60530368857775e+01\n4.74357246920861e-01\t1.39999999999997e+00 -3.44215434255212e-01 -1.05281465428559e-13 2.99849777600483e+00\t4.44792736461772e+00 2.26521476075770e+00 7.87789763538985e-01 2.60199899594511e+01\n4.75107912344200e-01\t1.39999999999997e+00 -3.50983140805023e-01 -9.69323150691370e-14 2.99822590828600e+00\t4.44312514452563e+00 2.27603087577066e+00 7.89285674439460e-01 2.59871775655326e+01\n4.75862821793107e-01\t1.39999999999996e+00 -3.57658661406905e-01 -9.12390455797599e-14 2.99795552194508e+00\t4.43831574929023e+00 2.28646904036921e+00 7.90849853335443e-01 2.59545834937781e+01\n4.76622706290556e-01\t1.39999999999997e+00 -3.64264884109991e-01 -7.60814492330420e-14 2.99769580615229e+00\t4.43348798877723e+00 2.29648797619069e+00 7.92399019534400e-01 2.59221460774367e+01\n4.77388464846336e-01\t1.39999999999997e+00 -3.70791572567434e-01 -5.89722940149655e-14 2.99744368212490e+00\t4.42864643508953e+00 2.30600963858247e+00 7.93909401673757e-01 2.58898910613671e+01\n4.78160583941780e-01\t1.39999999999996e+00 -3.77252526258674e-01 -5.10926916546678e-14 2.99720484857813e+00\t4.42378616608587e+00 2.31504581719576e+00 7.95342099148246e-01 2.58578285167858e+01\n4.78938389369143e-01\t1.39999999999997e+00 -3.83665291915554e-01 -5.26407327142793e-14 2.99697646182596e+00\t4.41890854925493e+00 2.32379079640277e+00 7.96725786438701e-01 2.58259169399971e+01\n4.79722493887432e-01\t1.39999999999997e+00 -3.89990683207767e-01 -5.17383201534631e-14 2.99674773395868e+00\t4.41402028007752e+00 2.33222972831182e+00 7.98088001747360e-01 2.57941378984808e+01\n4.80513055344974e-01\t1.39999999999996e+00 -3.96237170322215e-01 -4.01170071751469e-14 2.99651638321492e+00\t4.40911327553120e+00 2.34041833251619e+00 7.99473563113017e-01 2.57624377102654e+01\n4.81308938933647e-01\t1.39999999999996e+00 -4.02390117800181e-01 -2.35801135810026e-14 2.99628913791779e+00\t4.40418044395442e+00 2.34821824444834e+00 8.00841542317910e-01 2.57308075571496e+01\n4.82107165211744e-01\t1.39999999999997e+00 -4.08452102293527e-01 -7.79156211046197e-15 2.99607457819660e+00\t4.39923399318711e+00 2.35568444214477e+00 8.02170662924455e-01 2.56993297687926e+01\n4.82909761375149e-01\t1.39999999999996e+00 -4.14468877348472e-01 6.13347073855575e-15 2.99585858975281e+00\t4.39428414784810e+00 2.36298181781817e+00 8.03612065999275e-01 2.56679609983729e+01\n4.83717752228714e-01\t1.39999999999997e+00 -4.20405481963740e-01 1.61773959073151e-14 2.99563539492012e+00\t4.38933844204523e+00 2.37000827029425e+00 8.05181204175058e-01 2.56367382807489e+01\n4.84526738756332e-01\t1.39999999999996e+00 -4.26228543040484e-01 1.42918672233333e-14 2.99541245217424e+00\t4.38441322400549e+00 2.37667313075783e+00 8.06788317713216e-01 2.56057744566517e+01\n4.85339499598688e-01\t1.39999999999996e+00 -4.31947817056455e-01 1.67790042811926e-14 2.99518958537414e+00\t4.37946053653577e+00 2.38302905782899e+00 8.08447430083962e-01 2.55748049384858e+01\n4.86158627441728e-01\t1.39999999999997e+00 -4.37571372864022e-01 2.58985068266768e-14 2.99496575907946e+00\t4.37444391298785e+00 2.38910720589900e+00 8.10159343619288e-01 2.55435917440363e+01\n4.86984790777769e-01\t1.39999999999996e+00 -4.43059670421893e-01 2.91706694455714e-14 2.99474327958307e+00\t4.36933734172348e+00 2.39461504641304e+00 8.11904834815276e-01 2.55119286447820e+01\n4.87815617708345e-01\t1.39999999999996e+00 -4.48392194588332e-01 3.44164009982835e-14 2.99452533234453e+00\t4.36417638193123e+00 2.39932189132983e+00 8.13632380597078e-01 2.54799028612291e+01\n4.88651648021004e-01\t1.39999999999996e+00 -4.53596798224653e-01 3.64633368069642e-14 2.99431274477777e+00\t4.35896710077689e+00 2.40329532797993e+00 8.15358743509447e-01 2.54474892432652e+01\n4.89491193269207e-01\t1.39999999999996e+00 -4.58625178649690e-01 3.89064537399057e-14 2.99410828355565e+00\t4.35370360539048e+00 2.40648232889973e+00 8.17085904479105e-01 2.54146750462657e+01\n4.90333653506481e-01\t1.39999999999996e+00 -4.63420693977019e-01 3.51647431218872e-14 2.99391258870656e+00\t4.34839089012632e+00 2.40869125531303e+00 8.18799135914706e-01 2.53815128281044e+01\n4.91179570913328e-01\t1.39999999999996e+00 -4.67957059018231e-01 3.02418258185727e-14 2.99371365779350e+00\t4.34311017251203e+00 2.40976606581892e+00 8.20471634911724e-01 2.53483870894629e+01\n4.92028995761197e-01\t1.39999999999996e+00 -4.72245365653557e-01 2.79894627602753e-14 2.99352352129793e+00\t4.33782199842156e+00 2.40967848758118e+00 8.22090784515717e-01 2.53150711833116e+01\n4.92880599757050e-01\t1.39999999999996e+00 -4.76275621583008e-01 2.90239356958452e-14 2.99334589917028e+00\t4.33251368011774e+00 2.40843405509542e+00 8.23698291517452e-01 2.52815476294485e+01\n4.93734819248324e-01\t1.39999999999996e+00 -4.80056586356176e-01 2.56784062020875e-14 2.99318472319130e+00\t4.32716703001153e+00 2.40602922402612e+00 8.25273531330639e-01 2.52477577858932e+01\n4.94592540264710e-01\t1.39999999999996e+00 -4.83608772526224e-01 1.39543795989630e-14 2.99304224995272e+00\t4.32179906296596e+00 2.40254357009153e+00 8.26795632063375e-01 2.52138390341583e+01\n4.95452568360807e-01\t1.39999999999996e+00 -4.86950202345898e-01 -9.53769373220392e-17 2.99291507889666e+00\t4.31641433788041e+00 2.39806648906343e+00 8.28293363124338e-01 2.51799707273699e+01\n4.96313913003425e-01\t1.39999999999996e+00 -4.90115330267899e-01 -9.58171385712178e-15 2.99280741493775e+00\t4.31097498389617e+00 2.39272010097440e+00 8.29721026566611e-01 2.51460363577765e+01\n4.97177307000031e-01\t1.39999999999996e+00 -4.93133128456662e-01 -1.50402093469369e-14 2.99272124002855e+00\t4.30549335744084e+00 2.38663580701093e+00 8.31063976379010e-01 2.51121451021470e+01\n4.98043826458765e-01\t1.39999999999996e+00 -4.95997662313058e-01 -2.18119718968017e-14 2.99265224949725e+00\t4.30000940045259e+00 2.37989739728810e+00 8.32287799071069e-01 2.50785434710743e+01\n4.98914217244990e-01\t1.39999999999996e+00 -4.98701534552779e-01 -3.62872563072927e-14 2.99260310870969e+00\t4.29450718663400e+00 2.37247440951775e+00 8.33414330403940e-01 2.50451796863192e+01\n4.99789337733503e-01\t1.39999999999996e+00 -5.01241111767871e-01 -3.74171061801846e-14 2.99258256907545e+00\t4.28895305883383e+00 2.36431646089286e+00 8.34441859668372e-01 2.50118672913637e+01\n5.00669855083828e-01\t1.39999999999996e+00 -5.03632841474449e-01 -3.24428320644659e-14 2.99259083701171e+00\t4.28333132143650e+00 2.35558825205650e+00 8.35356196738630e-01 2.49784389021527e+01\n"
  },
  {
    "path": "tests/euler/check-mass-conservation_02.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = test\n\n  set enable compute quantities = true\n  set enable output full        = false\n\n  set final time                = 0.50\n  set timer granularity         = 0.50\n\n  set terminal update interval  = 0\n\n  set debug filename            = test-interior-R0000-space_averaged_time_series.dat\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n  set gamma     = 1.4\nend\n\nsubsection C - Discretization\n  set geometry        = annulus\n  set mesh refinement = 0\nend\n\nsubsection E - InitialValues\n  set configuration = uniform\n  set direction     =  1,  0\n  set position      =  0,  0\n\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.9\n  set cfl max            = 0.9\n  set cfl recovery strategy = none\n  set time stepping scheme  = ssprk 33\nend\n\nsubsection K - Quantities\n  set interior manifolds           = interior : 0. : space_averaged\nend\n"
  },
  {
    "path": "tests/euler/check-mass-conservation_03-dg.mpirun=1.threads=1.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\n9.97535267513177e-01\n9.97535267513185e-01\n9.97535267513181e-01\n9.97535267513185e-01\n9.97535267513179e-01\n"
  },
  {
    "path": "tests/euler/check-mass-conservation_03-dg.mpirun=4.threads=1.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\n9.97535267513182e-01\n9.97535267513183e-01\n9.97535267513182e-01\n9.97535267513181e-01\n9.97535267513182e-01\n"
  },
  {
    "path": "tests/euler/check-mass-conservation_03-dg.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = test\n\n  set enable compute quantities = true\n  set enable output full        = false\n\n  set final time                = 0.5\n  set timer granularity         = 0.5\n\n  set terminal update interval  = 0\n\n  set debug command             = ../../../../print_mass test-interior-R0000-space_averaged_time_series.dat\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n  set gamma     = 1.4\nend\n\nsubsection C - Discretization\n  set finite element ansatz = dG Q1\n\n  set geometry              = rectangular domain\n  set mesh refinement       = 6\n  subsection rectangular domain\n    set boundary condition bottom = periodic\n    set boundary condition left   = periodic\n    set boundary condition right  = periodic\n    set boundary condition top    = periodic\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,    1\n  set position      =  0.5,  0.5\nend\n\nsubsection F - HyperbolicModule\n  subsection indicator\n    set evc factor = 0.125\n  end\n  subsection limiter\n    set relaxation factor     = 4.\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.9\n  set cfl max            = 0.9\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n\nsubsection K - Quantities\n  set interior manifolds           = interior : 0. : space_averaged\nend\n"
  },
  {
    "path": "tests/euler/dynamic_boundary_conditions.prm",
    "content": "##\n#\n# Test dynamic boundary conditions by setting up a stationary isentropic\n# vortex on a very small domain. We use the polytropic gas equation of\n# state with gamma = 1.8.\n#\n##\n\nsubsection A - TimeLoop\n  set basename             = test\n\n  set enable compute error = true\n  set error normalize      = true\n  set error quantities     = rho, m_1, m_2, E\n\n  set final time           = 2.0\n  set timer granularity    = 2.0\n\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n\n  set gamma     = 1.80\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement     = 6\n\n  subsection rectangular domain\n    set boundary condition bottom = dynamic\n    set boundary condition left   = dynamic\n    set boundary condition right  = dynamic\n    set boundary condition top    = dynamic\n\n    set position bottom left      = -2, -2\n    set position top right        =  2,  2\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      =  0,  0\n\n  subsection isentropic vortex\n    set mach number = 0\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.5\n  set cfl max               = 0.5\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler/dynamic_boundary_conditions.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 4225\nt     = 2.000943757258066\nLinf  = 0.004200049906027967\nL1    = 0.0005634134289805881\nL2    = 0.0006975716927223317\n"
  },
  {
    "path": "tests/euler/hyperbolic_system.cc",
    "content": "#include <hyperbolic_system.h>\n#include <simd.h>\n\n#include <deal.II/base/vectorization.h>\n\n#include <iomanip>\n#include <iostream>\n\nusing namespace ryujin::Euler;\nusing namespace ryujin;\nusing namespace dealii;\n\n\ntemplate <int dim, typename Number>\nvoid test()\n{\n  std::cout << std::setprecision(10);\n  std::cout << std::scientific;\n\n  HyperbolicSystem hyperbolic_system;\n  const auto view = hyperbolic_system.view<dim, Number>();\n\n  using View = HyperbolicSystemView<dim, Number>;\n  using state_type = typename View::state_type;\n\n  const auto from_1d_state =\n      [&view](const dealii::Tensor<1, 3, Number> &state_1d) -> state_type {\n    const auto &rho = state_1d[0];\n    const auto &u = state_1d[1];\n    const auto &p = state_1d[2];\n\n    state_type state;\n\n    state[0] = rho;\n    state[1] = rho * u;\n    state[dim + 1] = p / (view.gamma() - 1.) + 0.5 * rho * u * u;\n\n    return state;\n  };\n\n  dealii::Tensor<1, 3, Number> state_1d;\n  state_1d[0] = view.gamma();\n  state_1d[1] = 3.;\n  state_1d[2] = 1.;\n  const auto U = from_1d_state(state_1d);\n\n  std::cout << \"dim = \" << dim << std::endl;\n  std::cout << \"momentum = \"                           //\n            << view.momentum(U)                        //\n            << std::endl;                              //\n  std::cout << \"internal_energy = \"                    //\n            << view.internal_energy(U)                 //\n            << std::endl;                              //\n  std::cout << \"internal_energy_derivative = \"         //\n            << view.internal_energy_derivative(U)      //\n            << std::endl;                              //\n  std::cout << \"pressure = \"                           //\n            << view.pressure(U)                        //\n            << std::endl;                              //\n  std::cout << \"specific_entropy = \"                   //\n            << view.specific_entropy(U)                //\n            << std::endl;                              //\n  std::cout << \"harten entropy = \"                     //\n            << view.harten_entropy(U)                  //\n            << std::endl;                              //\n  std::cout << \"harten_entropy_derivative = \"          //\n            << view.harten_entropy_derivative(U)       //\n            << std::endl;                              //\n  std::cout << \"mathematical entropy = \"               //\n            << view.mathematical_entropy(U)            //\n            << std::endl;                              //\n  std::cout << \"mathematical_entropy_derivative = \"    //\n            << view.mathematical_entropy_derivative(U) //\n            << std::endl;                              //\n  std::cout << \"f = \"                                  //\n            << view.f(U)                               //\n            << std::endl;                              //\n}\n\nint main()\n{\n  std::cout << \"\\ndouble:\\n\" << std::endl;\n  test<1, double>();\n  test<2, double>();\n  test<3, double>();\n\n  std::cout << \"float:\\n\" << std::endl;\n  test<1, float>();\n  test<2, float>();\n  test<3, float>();\n\n  std::cout << \"\\nVectorizedArray<double>\\n\" << std::endl;\n  test<1, VectorizedArray<double>>();\n  test<2, VectorizedArray<double>>();\n  test<3, VectorizedArray<double>>();\n\n  std::cout << \"\\nVectorizedArray<float>\\n\" << std::endl;\n  test<1, VectorizedArray<float>>();\n  test<2, VectorizedArray<float>>();\n  test<3, VectorizedArray<float>>();\n\n  return 0;\n}\n"
  },
  {
    "path": "tests/euler/hyperbolic_system.threads=2.output",
    "content": "\ndouble:\n\ndim = 1\nmomentum = 4.2000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\npressure = 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 2.8089484437e-01\nmathematical entropy = 1.0000000000e+00\nmathematical_entropy_derivative = 1.2857142857e+00 -8.5714285714e-01 2.8571428571e-01\nf = 4.2000000000e+00 1.3600000000e+01 2.9400000000e+01\ndim = 2\nmomentum = 4.2000000000e+00 0.0000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\npressure = 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 0.0000000000e+00 2.8089484437e-01\nmathematical entropy = 1.0000000000e+00\nmathematical_entropy_derivative = 1.2857142857e+00 -8.5714285714e-01 0.0000000000e+00 2.8571428571e-01\nf = 4.2000000000e+00 0.0000000000e+00 1.3600000000e+01 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 2.9400000000e+01 0.0000000000e+00\ndim = 3\nmomentum = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\npressure = 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 0.0000000000e+00 0.0000000000e+00 2.8089484437e-01\nmathematical entropy = 1.0000000000e+00\nmathematical_entropy_derivative = 1.2857142857e+00 -8.5714285714e-01 0.0000000000e+00 0.0000000000e+00 2.8571428571e-01\nf = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3600000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 2.9400000000e+01 0.0000000000e+00 0.0000000000e+00\nfloat:\n\ndim = 1\nmomentum = 4.1999998093e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\npressure = 1.0000003576e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656245232e+00 -8.4268438816e-01 2.8089478612e-01\nmathematical entropy = 1.0000002384e+00\nmathematical_entropy_derivative = 1.2857140303e+00 -8.5714268684e-01 2.8571423888e-01\nf = 4.1999998093e+00 1.3599999428e+01 2.9400001526e+01\ndim = 2\nmomentum = 4.1999998093e+00 0.0000000000e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\npressure = 1.0000003576e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656245232e+00 -8.4268438816e-01 0.0000000000e+00 2.8089478612e-01\nmathematical entropy = 1.0000002384e+00\nmathematical_entropy_derivative = 1.2857140303e+00 -8.5714268684e-01 0.0000000000e+00 2.8571423888e-01\nf = 4.1999998093e+00 0.0000000000e+00 1.3599999428e+01 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 2.9400001526e+01 0.0000000000e+00\ndim = 3\nmomentum = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\npressure = 1.0000003576e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656245232e+00 -8.4268438816e-01 0.0000000000e+00 0.0000000000e+00 2.8089478612e-01\nmathematical entropy = 1.0000002384e+00\nmathematical_entropy_derivative = 1.2857140303e+00 -8.5714268684e-01 0.0000000000e+00 0.0000000000e+00 2.8571423888e-01\nf = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 1.3599999428e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 2.9400001526e+01 0.0000000000e+00 0.0000000000e+00\n\nVectorizedArray<double>\n\ndim = 1\nmomentum = 4.2000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\npressure = 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 2.8089484437e-01\nmathematical entropy = 1.0000000000e+00\nmathematical_entropy_derivative = 1.2857142857e+00 -8.5714285714e-01 2.8571428571e-01\nf = 4.2000000000e+00 1.3600000000e+01 2.9400000000e+01\ndim = 2\nmomentum = 4.2000000000e+00 0.0000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\npressure = 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 0.0000000000e+00 2.8089484437e-01\nmathematical entropy = 1.0000000000e+00\nmathematical_entropy_derivative = 1.2857142857e+00 -8.5714285714e-01 0.0000000000e+00 2.8571428571e-01\nf = 4.2000000000e+00 0.0000000000e+00 1.3600000000e+01 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 2.9400000000e+01 0.0000000000e+00\ndim = 3\nmomentum = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\npressure = 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 0.0000000000e+00 0.0000000000e+00 2.8089484437e-01\nmathematical entropy = 1.0000000000e+00\nmathematical_entropy_derivative = 1.2857142857e+00 -8.5714285714e-01 0.0000000000e+00 0.0000000000e+00 2.8571428571e-01\nf = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3600000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 2.9400000000e+01 0.0000000000e+00 0.0000000000e+00\n\nVectorizedArray<float>\n\ndim = 1\nmomentum = 4.1999998093e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\npressure = 1.0000003576e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656245232e+00 -8.4268438816e-01 2.8089478612e-01\nmathematical entropy = 1.0000002384e+00\nmathematical_entropy_derivative = 1.2857140303e+00 -8.5714268684e-01 2.8571423888e-01\nf = 4.1999998093e+00 1.3599999428e+01 2.9400001526e+01\ndim = 2\nmomentum = 4.1999998093e+00 0.0000000000e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\npressure = 1.0000003576e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656245232e+00 -8.4268438816e-01 0.0000000000e+00 2.8089478612e-01\nmathematical entropy = 1.0000002384e+00\nmathematical_entropy_derivative = 1.2857140303e+00 -8.5714268684e-01 0.0000000000e+00 2.8571423888e-01\nf = 4.1999998093e+00 0.0000000000e+00 1.3599999428e+01 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 2.9400001526e+01 0.0000000000e+00\ndim = 3\nmomentum = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\npressure = 1.0000003576e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656245232e+00 -8.4268438816e-01 0.0000000000e+00 0.0000000000e+00 2.8089478612e-01\nmathematical entropy = 1.0000002384e+00\nmathematical_entropy_derivative = 1.2857140303e+00 -8.5714268684e-01 0.0000000000e+00 0.0000000000e+00 2.8571423888e-01\nf = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 1.3599999428e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 2.9400001526e+01 0.0000000000e+00 0.0000000000e+00\n"
  },
  {
    "path": "tests/euler/hyperbolic_system.threads=2.output.avx2",
    "content": "\ndouble:\n\ndim = 1\nmomentum = 4.2000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\npressure = 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 2.8089484437e-01\nmathematical entropy = 1.0000000000e+00\nmathematical_entropy_derivative = 1.2857142857e+00 -8.5714285714e-01 2.8571428571e-01\nf = 4.2000000000e+00 1.3600000000e+01 2.9400000000e+01\ndim = 2\nmomentum = 4.2000000000e+00 0.0000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\npressure = 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 0.0000000000e+00 2.8089484437e-01\nmathematical entropy = 1.0000000000e+00\nmathematical_entropy_derivative = 1.2857142857e+00 -8.5714285714e-01 0.0000000000e+00 2.8571428571e-01\nf = 4.2000000000e+00 0.0000000000e+00 1.3600000000e+01 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 2.9400000000e+01 0.0000000000e+00\ndim = 3\nmomentum = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\npressure = 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 0.0000000000e+00 0.0000000000e+00 2.8089484437e-01\nmathematical entropy = 1.0000000000e+00\nmathematical_entropy_derivative = 1.2857142857e+00 -8.5714285714e-01 0.0000000000e+00 0.0000000000e+00 2.8571428571e-01\nf = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3600000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 2.9400000000e+01 0.0000000000e+00 0.0000000000e+00\nfloat:\n\ndim = 1\nmomentum = 4.1999998093e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\npressure = 1.0000003576e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656244040e+00 -8.4268432856e-01 2.8089478612e-01\nmathematical entropy = 1.0000002384e+00\nmathematical_entropy_derivative = 1.2857140303e+00 -8.5714268684e-01 2.8571423888e-01\nf = 4.1999998093e+00 1.3599999428e+01 2.9400001526e+01\ndim = 2\nmomentum = 4.1999998093e+00 0.0000000000e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\npressure = 1.0000003576e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656244040e+00 -8.4268432856e-01 0.0000000000e+00 2.8089478612e-01\nmathematical entropy = 1.0000002384e+00\nmathematical_entropy_derivative = 1.2857140303e+00 -8.5714268684e-01 0.0000000000e+00 2.8571423888e-01\nf = 4.1999998093e+00 0.0000000000e+00 1.3599999428e+01 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 2.9400001526e+01 0.0000000000e+00\ndim = 3\nmomentum = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\npressure = 1.0000003576e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656244040e+00 -8.4268432856e-01 0.0000000000e+00 0.0000000000e+00 2.8089478612e-01\nmathematical entropy = 1.0000002384e+00\nmathematical_entropy_derivative = 1.2857140303e+00 -8.5714268684e-01 0.0000000000e+00 0.0000000000e+00 2.8571423888e-01\nf = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 1.3599999428e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 2.9400001526e+01 0.0000000000e+00 0.0000000000e+00\n\nVectorizedArray<double>\n\ndim = 1\nmomentum = 4.2000000000e+00 4.2000000000e+00 4.2000000000e+00 4.2000000000e+00\ninternal_energy = 2.5000000000e+00 2.5000000000e+00 2.5000000000e+00 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00\npressure = 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00 1.5608485265e+00 1.5608485265e+00 1.5608485265e+00\nharten entropy = 1.6853690662e+00 1.6853690662e+00 1.6853690662e+00 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 1.7656247360e+00 1.7656247360e+00 1.7656247360e+00 -8.4268453310e-01 -8.4268453310e-01 -8.4268453310e-01 -8.4268453310e-01 2.8089484437e-01 2.8089484437e-01 2.8089484437e-01 2.8089484437e-01\nmathematical entropy = 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00\nmathematical_entropy_derivative = 1.2857142857e+00 1.2857142857e+00 1.2857142857e+00 1.2857142857e+00 -8.5714285714e-01 -8.5714285714e-01 -8.5714285714e-01 -8.5714285714e-01 2.8571428571e-01 2.8571428571e-01 2.8571428571e-01 2.8571428571e-01\nf = 4.2000000000e+00 4.2000000000e+00 4.2000000000e+00 4.2000000000e+00 1.3600000000e+01 1.3600000000e+01 1.3600000000e+01 1.3600000000e+01 2.9400000000e+01 2.9400000000e+01 2.9400000000e+01 2.9400000000e+01\ndim = 2\nmomentum = 4.2000000000e+00 4.2000000000e+00 4.2000000000e+00 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00\ninternal_energy = 2.5000000000e+00 2.5000000000e+00 2.5000000000e+00 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00\npressure = 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00 1.5608485265e+00 1.5608485265e+00 1.5608485265e+00\nharten entropy = 1.6853690662e+00 1.6853690662e+00 1.6853690662e+00 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 1.7656247360e+00 1.7656247360e+00 1.7656247360e+00 -8.4268453310e-01 -8.4268453310e-01 -8.4268453310e-01 -8.4268453310e-01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 2.8089484437e-01 2.8089484437e-01 2.8089484437e-01 2.8089484437e-01\nmathematical entropy = 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00\nmathematical_entropy_derivative = 1.2857142857e+00 1.2857142857e+00 1.2857142857e+00 1.2857142857e+00 -8.5714285714e-01 -8.5714285714e-01 -8.5714285714e-01 -8.5714285714e-01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 2.8571428571e-01 2.8571428571e-01 2.8571428571e-01 2.8571428571e-01\nf = 4.2000000000e+00 4.2000000000e+00 4.2000000000e+00 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3600000000e+01 1.3600000000e+01 1.3600000000e+01 1.3600000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 2.9400000000e+01 2.9400000000e+01 2.9400000000e+01 2.9400000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00\ndim = 3\nmomentum = 4.2000000000e+00 4.2000000000e+00 4.2000000000e+00 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00\ninternal_energy = 2.5000000000e+00 2.5000000000e+00 2.5000000000e+00 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00\npressure = 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00 1.5608485265e+00 1.5608485265e+00 1.5608485265e+00\nharten entropy = 1.6853690662e+00 1.6853690662e+00 1.6853690662e+00 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 1.7656247360e+00 1.7656247360e+00 1.7656247360e+00 -8.4268453310e-01 -8.4268453310e-01 -8.4268453310e-01 -8.4268453310e-01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 2.8089484437e-01 2.8089484437e-01 2.8089484437e-01 2.8089484437e-01\nmathematical entropy = 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00\nmathematical_entropy_derivative = 1.2857142857e+00 1.2857142857e+00 1.2857142857e+00 1.2857142857e+00 -8.5714285714e-01 -8.5714285714e-01 -8.5714285714e-01 -8.5714285714e-01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 2.8571428571e-01 2.8571428571e-01 2.8571428571e-01 2.8571428571e-01\nf = 4.2000000000e+00 4.2000000000e+00 4.2000000000e+00 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3600000000e+01 1.3600000000e+01 1.3600000000e+01 1.3600000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 2.9400000000e+01 2.9400000000e+01 2.9400000000e+01 2.9400000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00\n\nVectorizedArray<float>\n\ndim = 1\nmomentum = 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00\ninternal_energy = 2.5000009537e+00 2.5000009537e+00 2.5000009537e+00 2.5000009537e+00 2.5000009537e+00 2.5000009537e+00 2.5000009537e+00 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00\npressure = 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00\nspecific_entropy = 1.5608491898e+00 1.5608491898e+00 1.5608491898e+00 1.5608491898e+00 1.5608491898e+00 1.5608491898e+00 1.5608491898e+00 1.5608491898e+00\nharten entropy = 1.6853692532e+00 1.6853692532e+00 1.6853692532e+00 1.6853692532e+00 1.6853692532e+00 1.6853692532e+00 1.6853692532e+00 1.6853692532e+00\nharten_entropy_derivative = 1.7656245232e+00 1.7656245232e+00 1.7656245232e+00 1.7656245232e+00 1.7656245232e+00 1.7656245232e+00 1.7656245232e+00 1.7656245232e+00 -8.4268438816e-01 -8.4268438816e-01 -8.4268438816e-01 -8.4268438816e-01 -8.4268438816e-01 -8.4268438816e-01 -8.4268438816e-01 -8.4268438816e-01 2.8089478612e-01 2.8089478612e-01 2.8089478612e-01 2.8089478612e-01 2.8089478612e-01 2.8089478612e-01 2.8089478612e-01 2.8089478612e-01\nmathematical entropy = 1.0000002384e+00 1.0000002384e+00 1.0000002384e+00 1.0000002384e+00 1.0000002384e+00 1.0000002384e+00 1.0000002384e+00 1.0000002384e+00\nmathematical_entropy_derivative = 1.2857140303e+00 1.2857140303e+00 1.2857140303e+00 1.2857140303e+00 1.2857140303e+00 1.2857140303e+00 1.2857140303e+00 1.2857140303e+00 -8.5714268684e-01 -8.5714268684e-01 -8.5714268684e-01 -8.5714268684e-01 -8.5714268684e-01 -8.5714268684e-01 -8.5714268684e-01 -8.5714268684e-01 2.8571423888e-01 2.8571423888e-01 2.8571423888e-01 2.8571423888e-01 2.8571423888e-01 2.8571423888e-01 2.8571423888e-01 2.8571423888e-01\nf = 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 1.3599999428e+01 1.3599999428e+01 1.3599999428e+01 1.3599999428e+01 1.3599999428e+01 1.3599999428e+01 1.3599999428e+01 1.3599999428e+01 2.9400001526e+01 2.9400001526e+01 2.9400001526e+01 2.9400001526e+01 2.9400001526e+01 2.9400001526e+01 2.9400001526e+01 2.9400001526e+01\ndim = 2\nmomentum = 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00\ninternal_energy = 2.5000009537e+00 2.5000009537e+00 2.5000009537e+00 2.5000009537e+00 2.5000009537e+00 2.5000009537e+00 2.5000009537e+00 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00\npressure = 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00\nspecific_entropy = 1.5608491898e+00 1.5608491898e+00 1.5608491898e+00 1.5608491898e+00 1.5608491898e+00 1.5608491898e+00 1.5608491898e+00 1.5608491898e+00\nharten entropy = 1.6853692532e+00 1.6853692532e+00 1.6853692532e+00 1.6853692532e+00 1.6853692532e+00 1.6853692532e+00 1.6853692532e+00 1.6853692532e+00\nharten_entropy_derivative = 1.7656245232e+00 1.7656245232e+00 1.7656245232e+00 1.7656245232e+00 1.7656245232e+00 1.7656245232e+00 1.7656245232e+00 1.7656245232e+00 -8.4268438816e-01 -8.4268438816e-01 -8.4268438816e-01 -8.4268438816e-01 -8.4268438816e-01 -8.4268438816e-01 -8.4268438816e-01 -8.4268438816e-01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 2.8089478612e-01 2.8089478612e-01 2.8089478612e-01 2.8089478612e-01 2.8089478612e-01 2.8089478612e-01 2.8089478612e-01 2.8089478612e-01\nmathematical entropy = 1.0000002384e+00 1.0000002384e+00 1.0000002384e+00 1.0000002384e+00 1.0000002384e+00 1.0000002384e+00 1.0000002384e+00 1.0000002384e+00\nmathematical_entropy_derivative = 1.2857140303e+00 1.2857140303e+00 1.2857140303e+00 1.2857140303e+00 1.2857140303e+00 1.2857140303e+00 1.2857140303e+00 1.2857140303e+00 -8.5714268684e-01 -8.5714268684e-01 -8.5714268684e-01 -8.5714268684e-01 -8.5714268684e-01 -8.5714268684e-01 -8.5714268684e-01 -8.5714268684e-01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 2.8571423888e-01 2.8571423888e-01 2.8571423888e-01 2.8571423888e-01 2.8571423888e-01 2.8571423888e-01 2.8571423888e-01 2.8571423888e-01\nf = 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3599999428e+01 1.3599999428e+01 1.3599999428e+01 1.3599999428e+01 1.3599999428e+01 1.3599999428e+01 1.3599999428e+01 1.3599999428e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 2.9400001526e+01 2.9400001526e+01 2.9400001526e+01 2.9400001526e+01 2.9400001526e+01 2.9400001526e+01 2.9400001526e+01 2.9400001526e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00\ndim = 3\nmomentum = 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00\ninternal_energy = 2.5000009537e+00 2.5000009537e+00 2.5000009537e+00 2.5000009537e+00 2.5000009537e+00 2.5000009537e+00 2.5000009537e+00 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00\npressure = 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00\nspecific_entropy = 1.5608491898e+00 1.5608491898e+00 1.5608491898e+00 1.5608491898e+00 1.5608491898e+00 1.5608491898e+00 1.5608491898e+00 1.5608491898e+00\nharten entropy = 1.6853692532e+00 1.6853692532e+00 1.6853692532e+00 1.6853692532e+00 1.6853692532e+00 1.6853692532e+00 1.6853692532e+00 1.6853692532e+00\nharten_entropy_derivative = 1.7656245232e+00 1.7656245232e+00 1.7656245232e+00 1.7656245232e+00 1.7656245232e+00 1.7656245232e+00 1.7656245232e+00 1.7656245232e+00 -8.4268438816e-01 -8.4268438816e-01 -8.4268438816e-01 -8.4268438816e-01 -8.4268438816e-01 -8.4268438816e-01 -8.4268438816e-01 -8.4268438816e-01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 2.8089478612e-01 2.8089478612e-01 2.8089478612e-01 2.8089478612e-01 2.8089478612e-01 2.8089478612e-01 2.8089478612e-01 2.8089478612e-01\nmathematical entropy = 1.0000002384e+00 1.0000002384e+00 1.0000002384e+00 1.0000002384e+00 1.0000002384e+00 1.0000002384e+00 1.0000002384e+00 1.0000002384e+00\nmathematical_entropy_derivative = 1.2857140303e+00 1.2857140303e+00 1.2857140303e+00 1.2857140303e+00 1.2857140303e+00 1.2857140303e+00 1.2857140303e+00 1.2857140303e+00 -8.5714268684e-01 -8.5714268684e-01 -8.5714268684e-01 -8.5714268684e-01 -8.5714268684e-01 -8.5714268684e-01 -8.5714268684e-01 -8.5714268684e-01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 2.8571423888e-01 2.8571423888e-01 2.8571423888e-01 2.8571423888e-01 2.8571423888e-01 2.8571423888e-01 2.8571423888e-01 2.8571423888e-01\nf = 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3599999428e+01 1.3599999428e+01 1.3599999428e+01 1.3599999428e+01 1.3599999428e+01 1.3599999428e+01 1.3599999428e+01 1.3599999428e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 2.9400001526e+01 2.9400001526e+01 2.9400001526e+01 2.9400001526e+01 2.9400001526e+01 2.9400001526e+01 2.9400001526e+01 2.9400001526e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00\n"
  },
  {
    "path": "tests/euler/hyperbolic_system.threads=2.output.avx512",
    "content": "\ndouble:\n\ndim = 1\nmomentum = 4.2\ninternal_energy = 2.5\ninternal_energy_derivative = 4.5 -3 1\npressure = 1\nspecific_entropy = 1.560848526\nharten entropy = 1.685369066\nharten_entropy_derivative = 1.765624736 -0.8426845331 0.2808948444\nmathematical entropy = 1\nmathematical_entropy_derivative = 1.285714286 -0.8571428571 0.2857142857\nf = 4.2 13.6 29.4\ndim = 2\nmomentum = 4.2 0\ninternal_energy = 2.5\ninternal_energy_derivative = 4.5 -3 -0 1\npressure = 1\nspecific_entropy = 1.560848526\nharten entropy = 1.685369066\nharten_entropy_derivative = 1.765624736 -0.8426845331 -0 0.2808948444\nmathematical entropy = 1\nmathematical_entropy_derivative = 1.285714286 -0.8571428571 -0 0.2857142857\nf = 4.2 0 13.6 0 0 1 29.4 0\ndim = 3\nmomentum = 4.2 0 0\ninternal_energy = 2.5\ninternal_energy_derivative = 4.5 -3 -0 -0 1\npressure = 1\nspecific_entropy = 1.560848526\nharten entropy = 1.685369066\nharten_entropy_derivative = 1.765624736 -0.8426845331 -0 -0 0.2808948444\nmathematical entropy = 1\nmathematical_entropy_derivative = 1.285714286 -0.8571428571 -0 -0 0.2857142857\nf = 4.2 0 0 13.6 0 0 0 1 0 0 0 1 29.4 0 0\nfloat:\n\ndim = 1\nmomentum = 4.199999809\ninternal_energy = 2.500000954\ninternal_energy_derivative = 4.5 -3 1\npressure = 1.000000358\nspecific_entropy = 1.56084919\nharten entropy = 1.685369253\nharten_entropy_derivative = 1.765624404 -0.8426843286 0.2808947861\nmathematical entropy = 1.000000238\nmathematical_entropy_derivative = 1.285714149 -0.8571427464 0.2857142389\nf = 4.199999809 13.59999943 29.40000153\ndim = 2\nmomentum = 4.199999809 0\ninternal_energy = 2.500000954\ninternal_energy_derivative = 4.5 -3 -0 1\npressure = 1.000000358\nspecific_entropy = 1.56084919\nharten entropy = 1.685369253\nharten_entropy_derivative = 1.765624523 -0.8426843882 -0 0.2808947861\nmathematical entropy = 1.000000238\nmathematical_entropy_derivative = 1.285714149 -0.8571427464 -0 0.2857142389\nf = 4.199999809 0 13.59999943 0 0 1.000000358 29.40000153 0\ndim = 3\nmomentum = 4.199999809 0 0\ninternal_energy = 2.500000954\ninternal_energy_derivative = 4.5 -3 -0 -0 1\npressure = 1.000000358\nspecific_entropy = 1.56084919\nharten entropy = 1.685369253\nharten_entropy_derivative = 1.765624523 -0.8426843882 -0 -0 0.2808947861\nmathematical entropy = 1.000000238\nmathematical_entropy_derivative = 1.285714149 -0.8571427464 -0 -0 0.2857142389\nf = 4.199999809 0 0 13.59999943 0 0 0 1.000000358 0 0 0 1.000000358 29.40000153 0 0\n\nVectorizedArray<double>\n\ndim = 1\nmomentum = 4.2 4.2 4.2 4.2 4.2 4.2 4.2 4.2\ninternal_energy = 2.5 2.5 2.5 2.5 2.5 2.5 2.5 2.5\ninternal_energy_derivative = 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 -3 -3 -3 -3 -3 -3 -3 -3 1 1 1 1 1 1 1 1\npressure = 1 1 1 1 1 1 1 1\nspecific_entropy = 1.560848526 1.560848526 1.560848526 1.560848526 1.560848526 1.560848526 1.560848526 1.560848526\nharten entropy = 1.685369066 1.685369066 1.685369066 1.685369066 1.685369066 1.685369066 1.685369066 1.685369066\nharten_entropy_derivative = 1.765624736 1.765624736 1.765624736 1.765624736 1.765624736 1.765624736 1.765624736 1.765624736 -0.8426845331 -0.8426845331 -0.8426845331 -0.8426845331 -0.8426845331 -0.8426845331 -0.8426845331 -0.8426845331 0.2808948444 0.2808948444 0.2808948444 0.2808948444 0.2808948444 0.2808948444 0.2808948444 0.2808948444\nmathematical entropy = 1 1 1 1 1 1 1 1\nmathematical_entropy_derivative = 1.285714286 1.285714286 1.285714286 1.285714286 1.285714286 1.285714286 1.285714286 1.285714286 -0.8571428571 -0.8571428571 -0.8571428571 -0.8571428571 -0.8571428571 -0.8571428571 -0.8571428571 -0.8571428571 0.2857142857 0.2857142857 0.2857142857 0.2857142857 0.2857142857 0.2857142857 0.2857142857 0.2857142857\nf = 4.2 4.2 4.2 4.2 4.2 4.2 4.2 4.2 13.6 13.6 13.6 13.6 13.6 13.6 13.6 13.6 29.4 29.4 29.4 29.4 29.4 29.4 29.4 29.4\ndim = 2\nmomentum = 4.2 4.2 4.2 4.2 4.2 4.2 4.2 4.2 0 0 0 0 0 0 0 0\ninternal_energy = 2.5 2.5 2.5 2.5 2.5 2.5 2.5 2.5\ninternal_energy_derivative = 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 -3 -3 -3 -3 -3 -3 -3 -3 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1\npressure = 1 1 1 1 1 1 1 1\nspecific_entropy = 1.560848526 1.560848526 1.560848526 1.560848526 1.560848526 1.560848526 1.560848526 1.560848526\nharten entropy = 1.685369066 1.685369066 1.685369066 1.685369066 1.685369066 1.685369066 1.685369066 1.685369066\nharten_entropy_derivative = 1.765624736 1.765624736 1.765624736 1.765624736 1.765624736 1.765624736 1.765624736 1.765624736 -0.8426845331 -0.8426845331 -0.8426845331 -0.8426845331 -0.8426845331 -0.8426845331 -0.8426845331 -0.8426845331 -0 -0 -0 -0 -0 -0 -0 -0 0.2808948444 0.2808948444 0.2808948444 0.2808948444 0.2808948444 0.2808948444 0.2808948444 0.2808948444\nmathematical entropy = 1 1 1 1 1 1 1 1\nmathematical_entropy_derivative = 1.285714286 1.285714286 1.285714286 1.285714286 1.285714286 1.285714286 1.285714286 1.285714286 -0.8571428571 -0.8571428571 -0.8571428571 -0.8571428571 -0.8571428571 -0.8571428571 -0.8571428571 -0.8571428571 -0 -0 -0 -0 -0 -0 -0 -0 0.2857142857 0.2857142857 0.2857142857 0.2857142857 0.2857142857 0.2857142857 0.2857142857 0.2857142857\nf = 4.2 4.2 4.2 4.2 4.2 4.2 4.2 4.2 0 0 0 0 0 0 0 0 13.6 13.6 13.6 13.6 13.6 13.6 13.6 13.6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 29.4 29.4 29.4 29.4 29.4 29.4 29.4 29.4 0 0 0 0 0 0 0 0\ndim = 3\nmomentum = 4.2 4.2 4.2 4.2 4.2 4.2 4.2 4.2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\ninternal_energy = 2.5 2.5 2.5 2.5 2.5 2.5 2.5 2.5\ninternal_energy_derivative = 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 -3 -3 -3 -3 -3 -3 -3 -3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1\npressure = 1 1 1 1 1 1 1 1\nspecific_entropy = 1.560848526 1.560848526 1.560848526 1.560848526 1.560848526 1.560848526 1.560848526 1.560848526\nharten entropy = 1.685369066 1.685369066 1.685369066 1.685369066 1.685369066 1.685369066 1.685369066 1.685369066\nharten_entropy_derivative = 1.765624736 1.765624736 1.765624736 1.765624736 1.765624736 1.765624736 1.765624736 1.765624736 -0.8426845331 -0.8426845331 -0.8426845331 -0.8426845331 -0.8426845331 -0.8426845331 -0.8426845331 -0.8426845331 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 0.2808948444 0.2808948444 0.2808948444 0.2808948444 0.2808948444 0.2808948444 0.2808948444 0.2808948444\nmathematical entropy = 1 1 1 1 1 1 1 1\nmathematical_entropy_derivative = 1.285714286 1.285714286 1.285714286 1.285714286 1.285714286 1.285714286 1.285714286 1.285714286 -0.8571428571 -0.8571428571 -0.8571428571 -0.8571428571 -0.8571428571 -0.8571428571 -0.8571428571 -0.8571428571 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 0.2857142857 0.2857142857 0.2857142857 0.2857142857 0.2857142857 0.2857142857 0.2857142857 0.2857142857\nf = 4.2 4.2 4.2 4.2 4.2 4.2 4.2 4.2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 13.6 13.6 13.6 13.6 13.6 13.6 13.6 13.6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 29.4 29.4 29.4 29.4 29.4 29.4 29.4 29.4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n\nVectorizedArray<float>\n\ndim = 1\nmomentum = 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809\ninternal_energy = 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954\ninternal_energy_derivative = 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1\npressure = 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358\nspecific_entropy = 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919\nharten entropy = 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253\nharten_entropy_derivative = 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861\nmathematical entropy = 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238\nmathematical_entropy_derivative = 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687\nf = 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153\ndim = 2\nmomentum = 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\ninternal_energy = 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954\ninternal_energy_derivative = 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1\npressure = 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358\nspecific_entropy = 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919\nharten entropy = 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253\nharten_entropy_derivative = 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861\nmathematical entropy = 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238\nmathematical_entropy_derivative = 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687\nf = 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\ndim = 3\nmomentum = 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\ninternal_energy = 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954 2.500000954\ninternal_energy_derivative = 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1\npressure = 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358\nspecific_entropy = 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919 1.56084919\nharten entropy = 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253 1.685369253\nharten_entropy_derivative = 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 1.765624404 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0.8426843286 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861 0.2808947861\nmathematical entropy = 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238 1.000000238\nmathematical_entropy_derivative = 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 1.285714149 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0.8571428061 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687 0.2857142687\nf = 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 4.199999809 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 13.59999943 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 1.000000358 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 29.40000153 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
  },
  {
    "path": "tests/euler/hyperbolic_system.threads=2.output.sse2",
    "content": "\ndouble:\n\ndim = 1\nmomentum = 4.2000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\npressure = 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 2.8089484437e-01\nmathematical entropy = 1.0000000000e+00\nmathematical_entropy_derivative = 1.2857142857e+00 -8.5714285714e-01 2.8571428571e-01\nf = 4.2000000000e+00 1.3600000000e+01 2.9400000000e+01\ndim = 2\nmomentum = 4.2000000000e+00 0.0000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\npressure = 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 0.0000000000e+00 2.8089484437e-01\nmathematical entropy = 1.0000000000e+00\nmathematical_entropy_derivative = 1.2857142857e+00 -8.5714285714e-01 0.0000000000e+00 2.8571428571e-01\nf = 4.2000000000e+00 0.0000000000e+00 1.3600000000e+01 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 2.9400000000e+01 0.0000000000e+00\ndim = 3\nmomentum = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\npressure = 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 0.0000000000e+00 0.0000000000e+00 2.8089484437e-01\nmathematical entropy = 1.0000000000e+00\nmathematical_entropy_derivative = 1.2857142857e+00 -8.5714285714e-01 0.0000000000e+00 0.0000000000e+00 2.8571428571e-01\nf = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3600000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 2.9400000000e+01 0.0000000000e+00 0.0000000000e+00\nfloat:\n\ndim = 1\nmomentum = 4.1999998093e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\npressure = 1.0000003576e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656245232e+00 -8.4268438816e-01 2.8089478612e-01\nmathematical entropy = 1.0000002384e+00\nmathematical_entropy_derivative = 1.2857140303e+00 -8.5714268684e-01 2.8571423888e-01\nf = 4.1999998093e+00 1.3599999428e+01 2.9400001526e+01\ndim = 2\nmomentum = 4.1999998093e+00 0.0000000000e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\npressure = 1.0000003576e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656245232e+00 -8.4268438816e-01 0.0000000000e+00 2.8089478612e-01\nmathematical entropy = 1.0000002384e+00\nmathematical_entropy_derivative = 1.2857140303e+00 -8.5714268684e-01 0.0000000000e+00 2.8571423888e-01\nf = 4.1999998093e+00 0.0000000000e+00 1.3599999428e+01 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 2.9400001526e+01 0.0000000000e+00\ndim = 3\nmomentum = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\npressure = 1.0000003576e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656245232e+00 -8.4268438816e-01 0.0000000000e+00 0.0000000000e+00 2.8089478612e-01\nmathematical entropy = 1.0000002384e+00\nmathematical_entropy_derivative = 1.2857140303e+00 -8.5714268684e-01 0.0000000000e+00 0.0000000000e+00 2.8571423888e-01\nf = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 1.3599999428e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 2.9400001526e+01 0.0000000000e+00 0.0000000000e+00\n\nVectorizedArray<double>\n\ndim = 1\nmomentum = 4.2000000000e+00 4.2000000000e+00\ninternal_energy = 2.5000000000e+00 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 4.5000000000e+00 -3.0000000000e+00 -3.0000000000e+00 1.0000000000e+00 1.0000000000e+00\npressure = 1.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00 1.5608485265e+00\nharten entropy = 1.6853690662e+00 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 1.7656247360e+00 -8.4268453310e-01 -8.4268453310e-01 2.8089484437e-01 2.8089484437e-01\nmathematical entropy = 1.0000000000e+00 1.0000000000e+00\nmathematical_entropy_derivative = 1.2857142857e+00 1.2857142857e+00 -8.5714285714e-01 -8.5714285714e-01 2.8571428571e-01 2.8571428571e-01\nf = 4.2000000000e+00 4.2000000000e+00 1.3600000000e+01 1.3600000000e+01 2.9400000000e+01 2.9400000000e+01\ndim = 2\nmomentum = 4.2000000000e+00 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00\ninternal_energy = 2.5000000000e+00 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 4.5000000000e+00 -3.0000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 1.0000000000e+00\npressure = 1.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00 1.5608485265e+00\nharten entropy = 1.6853690662e+00 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 1.7656247360e+00 -8.4268453310e-01 -8.4268453310e-01 0.0000000000e+00 0.0000000000e+00 2.8089484437e-01 2.8089484437e-01\nmathematical entropy = 1.0000000000e+00 1.0000000000e+00\nmathematical_entropy_derivative = 1.2857142857e+00 1.2857142857e+00 -8.5714285714e-01 -8.5714285714e-01 0.0000000000e+00 0.0000000000e+00 2.8571428571e-01 2.8571428571e-01\nf = 4.2000000000e+00 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3600000000e+01 1.3600000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 1.0000000000e+00 2.9400000000e+01 2.9400000000e+01 0.0000000000e+00 0.0000000000e+00\ndim = 3\nmomentum = 4.2000000000e+00 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00\ninternal_energy = 2.5000000000e+00 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 4.5000000000e+00 -3.0000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 1.0000000000e+00\npressure = 1.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00 1.5608485265e+00\nharten entropy = 1.6853690662e+00 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 1.7656247360e+00 -8.4268453310e-01 -8.4268453310e-01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 2.8089484437e-01 2.8089484437e-01\nmathematical entropy = 1.0000000000e+00 1.0000000000e+00\nmathematical_entropy_derivative = 1.2857142857e+00 1.2857142857e+00 -8.5714285714e-01 -8.5714285714e-01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 2.8571428571e-01 2.8571428571e-01\nf = 4.2000000000e+00 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3600000000e+01 1.3600000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 1.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 1.0000000000e+00 2.9400000000e+01 2.9400000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00\n\nVectorizedArray<float>\n\ndim = 1\nmomentum = 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00\ninternal_energy = 2.5000009537e+00 2.5000009537e+00 2.5000009537e+00 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00\npressure = 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00\nspecific_entropy = 1.5608491898e+00 1.5608491898e+00 1.5608491898e+00 1.5608491898e+00\nharten entropy = 1.6853692532e+00 1.6853692532e+00 1.6853692532e+00 1.6853692532e+00\nharten_entropy_derivative = 1.7656245232e+00 1.7656245232e+00 1.7656245232e+00 1.7656245232e+00 -8.4268438816e-01 -8.4268438816e-01 -8.4268438816e-01 -8.4268438816e-01 2.8089478612e-01 2.8089478612e-01 2.8089478612e-01 2.8089478612e-01\nmathematical entropy = 1.0000002384e+00 1.0000002384e+00 1.0000002384e+00 1.0000002384e+00\nmathematical_entropy_derivative = 1.2857140303e+00 1.2857140303e+00 1.2857140303e+00 1.2857140303e+00 -8.5714268684e-01 -8.5714268684e-01 -8.5714268684e-01 -8.5714268684e-01 2.8571423888e-01 2.8571423888e-01 2.8571423888e-01 2.8571423888e-01\nf = 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 1.3599999428e+01 1.3599999428e+01 1.3599999428e+01 1.3599999428e+01 2.9400001526e+01 2.9400001526e+01 2.9400001526e+01 2.9400001526e+01\ndim = 2\nmomentum = 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00\ninternal_energy = 2.5000009537e+00 2.5000009537e+00 2.5000009537e+00 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00\npressure = 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00\nspecific_entropy = 1.5608491898e+00 1.5608491898e+00 1.5608491898e+00 1.5608491898e+00\nharten entropy = 1.6853692532e+00 1.6853692532e+00 1.6853692532e+00 1.6853692532e+00\nharten_entropy_derivative = 1.7656245232e+00 1.7656245232e+00 1.7656245232e+00 1.7656245232e+00 -8.4268438816e-01 -8.4268438816e-01 -8.4268438816e-01 -8.4268438816e-01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 2.8089478612e-01 2.8089478612e-01 2.8089478612e-01 2.8089478612e-01\nmathematical entropy = 1.0000002384e+00 1.0000002384e+00 1.0000002384e+00 1.0000002384e+00\nmathematical_entropy_derivative = 1.2857140303e+00 1.2857140303e+00 1.2857140303e+00 1.2857140303e+00 -8.5714268684e-01 -8.5714268684e-01 -8.5714268684e-01 -8.5714268684e-01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 2.8571423888e-01 2.8571423888e-01 2.8571423888e-01 2.8571423888e-01\nf = 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3599999428e+01 1.3599999428e+01 1.3599999428e+01 1.3599999428e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 2.9400001526e+01 2.9400001526e+01 2.9400001526e+01 2.9400001526e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00\ndim = 3\nmomentum = 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00\ninternal_energy = 2.5000009537e+00 2.5000009537e+00 2.5000009537e+00 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 4.5000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00 1.0000000000e+00\npressure = 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00\nspecific_entropy = 1.5608491898e+00 1.5608491898e+00 1.5608491898e+00 1.5608491898e+00\nharten entropy = 1.6853692532e+00 1.6853692532e+00 1.6853692532e+00 1.6853692532e+00\nharten_entropy_derivative = 1.7656245232e+00 1.7656245232e+00 1.7656245232e+00 1.7656245232e+00 -8.4268438816e-01 -8.4268438816e-01 -8.4268438816e-01 -8.4268438816e-01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 2.8089478612e-01 2.8089478612e-01 2.8089478612e-01 2.8089478612e-01\nmathematical entropy = 1.0000002384e+00 1.0000002384e+00 1.0000002384e+00 1.0000002384e+00\nmathematical_entropy_derivative = 1.2857140303e+00 1.2857140303e+00 1.2857140303e+00 1.2857140303e+00 -8.5714268684e-01 -8.5714268684e-01 -8.5714268684e-01 -8.5714268684e-01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 2.8571423888e-01 2.8571423888e-01 2.8571423888e-01 2.8571423888e-01\nf = 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3599999428e+01 1.3599999428e+01 1.3599999428e+01 1.3599999428e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 1.0000003576e+00 2.9400001526e+01 2.9400001526e+01 2.9400001526e+01 2.9400001526e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00\n"
  },
  {
    "path": "tests/euler/initial_state_exact_riemann_solution.cc",
    "content": "// force distinct symbols in test\n#define Euler EulerTest\n\n#include <hyperbolic_system.h>\n#include <multicomponent_vector.h>\n#include <simd.h>\n\n#define DEBUG_SOLUTION\n#include <description.h>\n#include <initial_state_exact_riemann_solution.h>\n\nusing namespace ryujin::EulerInitialStates;\nusing namespace ryujin::Euler;\nusing namespace ryujin;\nusing namespace dealii;\n\nint main()\n{\n  constexpr int dim = 1;\n\n  HyperbolicSystem hyperbolic_system;\n\n  using state_type = HyperbolicSystemView<dim, double>::state_type;\n\n  std::cout << std::setprecision(16);\n  std::cout << std::scientific;\n\n  std::cout << \"Calling default constructor...\" << std::endl;\n  ExactRiemannSolution<Description, dim, double> initial_state(\n      hyperbolic_system, \"\");\n\n  {\n    std::cout << \"Resetting parameters...\" << std::endl;\n    std::stringstream parameters;\n    parameters << \"subsection exact riemann solution\\n\"\n               << \"set primitive state left =     1., 0., 0.666666666667e-1\\n\"\n               << \"set primitive state right = 1.e-3, 0., 0.666666666667e-10\\n\"\n               << \"end\\n\"\n               << std::endl;\n    ParameterAcceptor::initialize(parameters);\n  }\n\n  std::cout << \"Calling compute...\" << std::endl;\n  const auto test = [&](const double x, const double t) {\n    std::cout << \"Position x = \" << x << \", t = \" << t << std::endl;\n    auto state = initial_state.compute(Point<dim>{x}, t);\n    std::cout << \"  --> \" << state << std::endl;\n  };\n\n  for (double t : {0.0, 0.5, 1.0, 1.5, 2.0, 2.5}) {\n    test(-0.5, t);\n    test(0.5, t);\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "tests/euler/initial_state_exact_riemann_solution.threads=2.output",
    "content": "Calling default constructor...\nComputing p_star with a bisection method.\nAfter 0 iterations:\np_star =      1.0000000000000000e+00\nphi(p_star) = 0.0000000000000000e+00\n|p_2 - p_1| = 0.0000000000000000e+00\nleft data          = 1.3999999999999999e+00 0.0000000000000000e+00 1.0000000000000000e+00\nright data       = 1.3999999999999999e+00 0.0000000000000000e+00 1.0000000000000000e+00\np_star           = 1.0000000000000000e+00\nu_star           = 0.0000000000000000e+00\nVerifying u_star = 0.0000000000000000e+00\nlambda_left_minus  =  -1.0000000000000000e+00\nlambda_left_plus   =  -1.0000000000000000e+00\nlambda_right_minus = 1.0000000000000000e+00\nlambda_right_plus  =  1.0000000000000000e+00\nResetting parameters...\nComputing p_star with a bisection method.\n\niter: 0\np_1: 6.6666666666700002e-11\np_2: 6.6666666666700000e-02\nphi_1: -1.4484074490600287e+00\nphi_2: 7.4535599169264728e+00\n\niter: 1\np_1: 6.6666666666700002e-11\np_2: 3.3333333366683336e-02\nphi_1: -1.4484074490600287e+00\nphi_2: 5.1264532767775490e+00\n\niter: 2\np_1: 6.6666666666700002e-11\np_2: 1.6666666716675001e-02\nphi_1: -1.4484074490600287e+00\nphi_2: 3.4523376755442414e+00\n\niter: 3\np_1: 6.6666666666700002e-11\np_2: 8.3333333916708330e-03\nphi_1: -1.4484074490600287e+00\nphi_2: 2.2426530247155418e+00\n\niter: 4\np_1: 6.6666666666700002e-11\np_2: 4.1666667291687500e-03\nphi_1: -1.4484074490600287e+00\nphi_2: 1.3638129852432002e+00\n\niter: 5\np_1: 6.6666666666700002e-11\np_2: 2.0833333979177085e-03\nphi_1: -1.4484074490600287e+00\nphi_2: 7.2112749621671113e-01\n\niter: 6\np_1: 6.6666666666700002e-11\np_2: 1.0416667322921875e-03\nphi_1: -1.4484074490600287e+00\nphi_2: 2.4743202638280037e-01\n\niter: 7\np_1: 5.2083339947942714e-04\np_2: 1.0416667322921875e-03\nphi_1: -1.0495480562113624e-01\nphi_2: 2.4743202638280037e-01\n\niter: 8\np_1: 5.2083339947942714e-04\np_2: 7.8125006588580727e-04\nphi_1: -1.0495480562113624e-01\nphi_2: 8.8655120960877465e-02\n\niter: 9\np_1: 6.5104173268261726e-04\np_2: 7.8125006588580727e-04\nphi_1: -2.4539090701988231e-03\nphi_2: 8.8655120960877465e-02\n\niter: 10\np_1: 6.5104173268261726e-04\np_2: 7.1614589928421226e-04\nphi_1: -2.4539090701988231e-03\nphi_2: 4.4306661808490966e-02\n\niter: 11\np_1: 6.5104173268261726e-04\np_2: 6.8359381598341476e-04\nphi_1: -2.4539090701988231e-03\nphi_2: 2.1250807053167176e-02\n\niter: 12\np_1: 6.5104173268261726e-04\np_2: 6.6731777433301606e-04\nphi_1: -2.4539090701988231e-03\nphi_2: 9.4827388690529313e-03\n\niter: 13\np_1: 6.5104173268261726e-04\np_2: 6.5917975350781666e-04\nphi_1: -2.4539090701988231e-03\nphi_2: 3.5359076446351834e-03\n\niter: 14\np_1: 6.5104173268261726e-04\np_2: 6.5511074309521696e-04\nphi_1: -2.4539090701988231e-03\nphi_2: 5.4642650927494429e-04\n\niter: 15\np_1: 6.5307623788891716e-04\np_2: 6.5511074309521696e-04\nphi_1: -9.5237762345745658e-04\nphi_2: 5.4642650927494429e-04\n\niter: 16\np_1: 6.5409349049206701e-04\np_2: 6.5511074309521696e-04\nphi_1: -2.0263550367172201e-04\nphi_2: 5.4642650927494429e-04\n\niter: 17\np_1: 6.5409349049206701e-04\np_2: 6.5460211679364204e-04\nphi_1: -2.0263550367172201e-04\nphi_2: 1.7198040893917632e-04\n\niter: 18\np_1: 6.5434780364285452e-04\np_2: 6.5460211679364204e-04\nphi_1: -1.5306307446838474e-05\nphi_2: 1.7198040893917632e-04\n\niter: 19\np_1: 6.5434780364285452e-04\np_2: 6.5447496021824828e-04\nphi_1: -1.5306307446838474e-05\nphi_2: 7.8342359051797317e-05\n\niter: 20\np_1: 6.5434780364285452e-04\np_2: 6.5441138193055135e-04\nphi_1: -1.5306307446838474e-05\nphi_2: 3.1519353088094171e-05\n\niter: 21\np_1: 6.5434780364285452e-04\np_2: 6.5437959278670293e-04\nphi_1: -1.5306307446838474e-05\nphi_2: 8.1068546681217768e-06\n\niter: 22\np_1: 6.5436369821477878e-04\np_2: 6.5437959278670293e-04\nphi_1: -3.5996434243346087e-06\nphi_2: 8.1068546681217768e-06\n\niter: 23\np_1: 6.5436369821477878e-04\np_2: 6.5437164550074091e-04\nphi_1: -3.5996434243346087e-06\nphi_2: 2.2536263629691078e-06\n\niter: 24\np_1: 6.5436767185775985e-04\np_2: 6.5437164550074091e-04\nphi_1: -6.7300334549713625e-07\nphi_2: 2.2536263629691078e-06\n\niter: 25\np_1: 6.5436767185775985e-04\np_2: 6.5436965867925038e-04\nphi_1: -6.7300334549713625e-07\nphi_2: 7.9031280508790047e-07\n\niter: 26\np_1: 6.5436767185775985e-04\np_2: 6.5436866526850511e-04\nphi_1: -6.7300334549713625e-07\nphi_2: 5.8655053924994149e-08\n\niter: 27\np_1: 6.5436816856313243e-04\np_2: 6.5436866526850511e-04\nphi_1: -3.0717406462876795e-07\nphi_2: 5.8655053924994149e-08\n\niter: 28\np_1: 6.5436841691581872e-04\np_2: 6.5436866526850511e-04\nphi_1: -1.2425948514582785e-07\nphi_2: 5.8655053924994149e-08\n\niter: 29\np_1: 6.5436854109216197e-04\np_2: 6.5436866526850511e-04\nphi_1: -3.2802210503390938e-08\nphi_2: 5.8655053924994149e-08\n\niter: 30\np_1: 6.5436854109216197e-04\np_2: 6.5436860318033360e-04\nphi_1: -3.2802210503390938e-08\nphi_2: 1.2926423154091538e-08\n\niter: 31\np_1: 6.5436857213624778e-04\np_2: 6.5436860318033360e-04\nphi_1: -9.9378933970939443e-09\nphi_2: 1.2926423154091538e-08\n\niter: 32\np_1: 6.5436857213624778e-04\np_2: 6.5436858765829074e-04\nphi_1: -9.9378933970939443e-09\nphi_2: 1.4942649340099479e-09\n\niter: 33\np_1: 6.5436857989726932e-04\np_2: 6.5436858765829074e-04\nphi_1: -4.2218141205196957e-09\nphi_2: 1.4942649340099479e-09\n\niter: 34\np_1: 6.5436858377778003e-04\np_2: 6.5436858765829074e-04\nphi_1: -1.3637745377437227e-09\nphi_2: 1.4942649340099479e-09\n\niter: 35\np_1: 6.5436858377778003e-04\np_2: 6.5436858571803539e-04\nphi_1: -1.3637745377437227e-09\nphi_2: 6.5245142621961350e-11\n\niter: 36\np_1: 6.5436858474790765e-04\np_2: 6.5436858571803539e-04\nphi_1: -6.4926475307203191e-10\nphi_2: 6.5245142621961350e-11\n\niter: 37\np_1: 6.5436858523297147e-04\np_2: 6.5436858571803539e-04\nphi_1: -2.9200974971388405e-10\nphi_2: 6.5245142621961350e-11\n\niter: 38\np_1: 6.5436858547550337e-04\np_2: 6.5436858571803539e-04\nphi_1: -1.1338241456826381e-10\nphi_2: 6.5245142621961350e-11\n\niter: 39\np_1: 6.5436858559676943e-04\np_2: 6.5436858571803539e-04\nphi_1: -2.4068746995453694e-11\nphi_2: 6.5245142621961350e-11\n\niter: 40\np_1: 6.5436858559676943e-04\np_2: 6.5436858565740236e-04\nphi_1: -2.4068746995453694e-11\nphi_2: 2.0588197813253828e-11\n\niter: 41\np_1: 6.5436858562708589e-04\np_2: 6.5436858565740236e-04\nphi_1: -1.7402745910999329e-12\nphi_2: 2.0588197813253828e-11\n\niter: 42\np_1: 6.5436858562708589e-04\np_2: 6.5436858564224413e-04\nphi_1: -1.7402745910999329e-12\nphi_2: 9.4240171222281788e-12\n\niter: 43\np_1: 6.5436858562708589e-04\np_2: 6.5436858563466501e-04\nphi_1: -1.7402745910999329e-12\nphi_2: 3.8419267767153542e-12\n\niter: 44\np_1: 6.5436858562708589e-04\np_2: 6.5436858563087540e-04\nphi_1: -1.7402745910999329e-12\nphi_2: 1.0508260928077107e-12\n\niter: 45\np_1: 6.5436858562898065e-04\np_2: 6.5436858563087540e-04\nphi_1: -3.4472424914611111e-13\nphi_2: 1.0508260928077107e-12\n\niter: 46\np_1: 6.5436858562898065e-04\np_2: 6.5436858562992802e-04\nphi_1: -3.4472424914611111e-13\nphi_2: 3.5293989952833726e-13\n\niter: 47\np_1: 6.5436858562898065e-04\np_2: 6.5436858562945433e-04\nphi_1: -3.4472424914611111e-13\nphi_2: 4.2188474935755949e-15\n\niter: 48\np_1: 6.5436858562921744e-04\np_2: 6.5436858562945433e-04\nphi_1: -1.7019718967503650e-13\nphi_2: 4.2188474935755949e-15\n\niter: 49\np_1: 6.5436858562933583e-04\np_2: 6.5436858562945433e-04\nphi_1: -8.3155704544424225e-14\nphi_2: 4.2188474935755949e-15\n\niter: 50\np_1: 6.5436858562939503e-04\np_2: 6.5436858562945433e-04\nphi_1: -3.9523939676655573e-14\nphi_2: 4.2188474935755949e-15\n\niter: 51\np_1: 6.5436858562942474e-04\np_2: 6.5436858562945433e-04\nphi_1: -1.7652546091539989e-14\nphi_2: 4.2188474935755949e-15\n\niter: 52\np_1: 6.5436858562943948e-04\np_2: 6.5436858562945433e-04\nphi_1: -6.6613381477509392e-15\nphi_2: 4.2188474935755949e-15\n\niter: 53\np_1: 6.5436858562944685e-04\np_2: 6.5436858562945433e-04\nphi_1: -1.3322676295501878e-15\nphi_2: 4.2188474935755949e-15\n\niter: 54\np_1: 6.5436858562944685e-04\np_2: 6.5436858562945054e-04\nphi_1: -1.3322676295501878e-15\nphi_2: 1.4432899320127035e-15\n\niter: 55\np_1: 6.5436858562944685e-04\np_2: 6.5436858562944870e-04\nphi_1: -1.3322676295501878e-15\nphi_2: 1.1102230246251565e-16\nAfter 56 iterations:\np_star =      6.5436858562944870e-04\nphi(p_star) = 1.1102230246251565e-16\n|p_2 - p_1| = 9.7578195523695399e-19\nleft data          = 1.0000000000000000e+00 0.0000000000000000e+00 6.6666666666700000e-02\nright data       = 1.0000000000000000e-03 0.0000000000000000e+00 6.6666666666700002e-11\np_star           = 6.5436858562944870e-04\nu_star           = 7.3844907361364098e-01\nVerifying u_star = 7.3844907361364109e-01\nlambda_left_minus  =  -3.0550504633046571e-01\nlambda_left_plus   =  5.8063384200590340e-01\nlambda_right_minus = 8.8613899366220850e-01\nlambda_right_plus  =  8.8613899366220850e-01\nCalling compute...\nPosition x = -5.0000000000000000e-01, t = 0.0000000000000000e+00\nLeft primitive state: 1.0000000000000000e+00 0.0000000000000000e+00 6.6666666666700000e-02\n  --> 1.0000000000000000e+00 0.0000000000000000e+00 1.6666666666675004e-01\nPosition x = 5.0000000000000000e-01, t = 0.0000000000000000e+00\nRight primitive state: 1.0000000000000000e-03 0.0000000000000000e+00 6.6666666666700002e-11\n  --> 1.0000000000000000e-03 0.0000000000000000e+00 1.6666666666675005e-10\nPosition x = -5.0000000000000000e-01, t = 5.0000000000000000e-01\nLeft primitive state: 1.0000000000000000e+00 0.0000000000000000e+00 6.6666666666700000e-02\n  --> 1.0000000000000000e+00 0.0000000000000000e+00 1.6666666666675004e-01\nPosition x = 5.0000000000000000e-01, t = 5.0000000000000000e-01\nRight primitive state: 1.0000000000000000e-03 0.0000000000000000e+00 6.6666666666700002e-11\n  --> 1.0000000000000000e-03 0.0000000000000000e+00 1.6666666666675005e-10\nPosition x = -5.0000000000000000e-01, t = 1.0000000000000000e+00\nLeft primitive state: 1.0000000000000000e+00 0.0000000000000000e+00 6.6666666666700000e-02\n  --> 1.0000000000000000e+00 0.0000000000000000e+00 1.6666666666675004e-01\nPosition x = 5.0000000000000000e-01, t = 1.0000000000000000e+00\nLeft expansion state: 5.5349573922065014e-02 6.7125420527538815e-01 1.1594946145153274e-03\n  --> 5.5349573922065014e-02 3.7153634255387098e-02 1.5368503153884470e-02\nPosition x = -5.0000000000000000e-01, t = 1.5000000000000000e+00\nLeft primitive state: 1.0000000000000000e+00 0.0000000000000000e+00 6.6666666666700000e-02\n  --> 1.0000000000000000e+00 0.0000000000000000e+00 1.6666666666675004e-01\nPosition x = 5.0000000000000000e-01, t = 1.5000000000000000e+00\nLeft expansion state: 1.1736062374560188e-01 5.3236531638649931e-01 3.3207800673750007e-03\n  --> 1.1736062374560188e-01 6.2478725591644253e-02 2.4932703426947984e-02\nPosition x = -5.0000000000000000e-01, t = 2.0000000000000000e+00\nLeft expansion state: 8.5749318554969178e-01 4.6254205275388170e-02 5.3756587212215864e-02\n  --> 8.5749318554969178e-01 3.9662665826661961e-02 1.3530875057399747e-01\nPosition x = 5.0000000000000000e-01, t = 2.0000000000000000e+00\nLeft expansion state: 1.6443688397993841e-01 4.6292087194205467e-01 5.3248536353566103e-03\n  --> 1.6443688397993841e-01 7.6121265711427577e-02 3.0931195436624967e-02\nPosition x = -5.0000000000000000e-01, t = 2.5000000000000000e+00\nLeft expansion state: 7.4348772513377437e-01 8.7920871942054793e-02 4.4024260016096786e-02\n  --> 7.4348772513377437e-01 6.5368089071976207e-02 1.1293425973443902e-01\nPosition x = 5.0000000000000000e-01, t = 2.5000000000000000e+00\nLeft expansion state: 1.9923504298574909e-01 4.2125420527538804e-01 6.9665981470314114e-03\n  --> 1.9923504298574909e-01 8.3928599695969511e-02 3.5094133149979431e-02\n"
  },
  {
    "path": "tests/euler/limiter.cc",
    "content": "// force distinct symbols in test\n#define Euler EulerTest\n\n#include <hyperbolic_system.h>\n#include <multicomponent_vector.h>\n#include <simd.h>\n\n#include <limiter.h>\n\n#define DEBUG_EXPENSIVE_BOUNDS_CHECK\n#define DEBUG_OUTPUT\n#define DEBUG_OUTPUT_LIMITER\n#include <limiter.template.h>\n\nusing namespace ryujin::Euler;\nusing namespace ryujin;\nusing namespace dealii;\n\nint main()\n{\n  constexpr int dim = 1;\n\n  HyperbolicSystem hyperbolic_system;\n  Limiter<dim, double>::Parameters limiter_parameters;\n\n  using state_type = HyperbolicSystemView<dim, double>::state_type;\n\n  using bounds_type = Limiter<dim, double>::Bounds;\n\n  static constexpr unsigned int n_precomputed_values =\n      HyperbolicSystemView<dim, double>::n_precomputed_values;\n\n  using precomputed_type =\n      Vectors::MultiComponentVector<double, n_precomputed_values>;\n  precomputed_type dummy;\n\n  Limiter<dim, double> limiter(hyperbolic_system, limiter_parameters, dummy);\n\n  const auto view = hyperbolic_system.template view<dim, double>();\n\n  const auto test =\n      [&](const state_type &U, const state_type &P, const bounds_type &bounds) {\n        std::cout << \"State: \" << U\n                  << \"\\nSpecific entropy: \" << view.specific_entropy(U)\n                  << \"\\nBounds: \" << bounds[0] << \" \" << bounds[1] << \" \"\n                  << bounds[2] << std::endl;\n\n        const auto &[l, success] = limiter.limit(bounds, U, P);\n\n        std::cout << \"l: \" << l;\n        if (success)\n          std::cout << \"\\nSuccess!\";\n        else\n          std::cout << \"\\nFailure!\";\n        std::cout << std::endl;\n      };\n\n  std::cout << std::setprecision(16);\n  std::cout << std::scientific;\n\n  {\n    std::cout << \"\\n\\nChecking exceptional cases:\" << std::endl;\n\n    std::cout << \"\\nMinimum density violation:\" << std::endl;\n    auto U = state_type{{0.8, 1.4, 3.0}};\n    auto P = state_type{{-0.1, 0.1, 0.1}};\n    auto bounds = bounds_type{0.9, 1.1, 2.0};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMinimum density violation (eps):\" << std::endl;\n    U = state_type{{0.9 - 1.0e-10, 1.4, 3.0}};\n    P = state_type{{-1.0e-20, 0.1, 0.1}};\n    bounds = bounds_type{0.9, 1.1, 2.0};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMaximum density violation:\" << std::endl;\n    U = state_type{{1.2, 1.4, 3.0}};\n    P = state_type{{0.1, 0.1, 0.1}};\n    bounds = bounds_type{0.9, 1.1, 2.0};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMaximum density violation (eps):\" << std::endl;\n    U = state_type{{1.1 + 1.0e-10, 1.4, 3.0}};\n    P = state_type{{1.0e-20, 0.1, 0.1}};\n    bounds = bounds_type{0.9, 1.1, 2.0};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMinimum entropy violation:\" << std::endl;\n    U = state_type{{1.0, 1.4, 2.8}};\n    P = state_type{{0.1, 0.1, -0.1}};\n    bounds = bounds_type{0.9, 1.1, 2.0};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMinimum entropy violation (eps):\" << std::endl;\n    U = state_type{{1.0, 1.4, 2.8}};\n    P = state_type{{0.1, 0.1, -1.0e-20}};\n    bounds = bounds_type{0.9, 1.1, 1.82 + 1.e-10};\n    test(U, P, bounds);\n  }\n\n  {\n    std::cout << \"\\n\\nChecking individual limiter components:\" << std::endl;\n\n    std::cout << \"\\nMinimum density bound\" << std::endl;\n    auto U = state_type{{1.0, 1.4, 3.0}};\n    auto P = state_type{{-0.2, 0.1, 0.1}};\n    auto bounds = bounds_type{0.9, 1.1, 2.0};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMinimum density bound (eps):\" << std::endl;\n    U = state_type{{0.9 + 1.0e-10, 1.4, 3.0}};\n    P = state_type{{-5.0e-10, 0.1, 0.1}};\n    bounds = bounds_type{0.9, 1.1, 2.0};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMaximum density bound\" << std::endl;\n    U = state_type{{1.0, 1.4, 3.0}};\n    P = state_type{{0.2, 0.1, 0.1}};\n    bounds = bounds_type{0.9, 1.1, 1.0};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMaximum density bound (eps):\" << std::endl;\n    U = state_type{{1.1 - 1.0e-10, 1.4, 3.0}};\n    P = state_type{{5.0e-10, 0.1, 0.1}};\n    bounds = bounds_type{0.9, 1.1, 1.0};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMinimum entropy bound\" << std::endl;\n    U = state_type{{1.0, 1.4, 2.8}};\n    P = state_type{{0.1, 0.1, -0.3}};\n    bounds = bounds_type{0.9, 1.1, 1.8};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMinimum entropy bound (eps):\" << std::endl;\n    U = state_type{{1.0, 1.4, 2.8}};\n    P = state_type{{0.1, 0.1, -4.0e-10}};\n    bounds = bounds_type{0.9, 1.1, 1.82 - 1.e-10};\n    test(U, P, bounds);\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "tests/euler/limiter.threads=2.output",
    "content": "\n\nChecking exceptional cases:\n\nMinimum density violation:\nState: 8.0000000000000004e-01 1.3999999999999999e+00 3.0000000000000000e+00\nSpecific entropy: 2.4258971015616480e+00\nBounds: 9.0000000000000002e-01 1.1000000000000001e+00 2.0000000000000000e+00\nBounds violation: low-order density (critical)!\n\t\trho min:         0.9000000000000000\n\t\trho min (delta): 0.1000000000000000\n\t\trho:             0.8000000000000000\n\t\trho max (delta): 0.0000000000000000\n\t\trho max:         1.1000000000000001\n\nBounds violation: high-order density!\n\t\trho min:         0.9000000000000000\n\t\trho min (delta): 0.1000000000000000\n\t\trho:             0.8000000000000000\n\t\trho max (delta): 0.0000000000000000\n\t\trho max:         1.1000000000000001\n\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.0000000000000000\nbreak: t_l and t_r within tolerance\npsi_l:       0.2492990670660760\npsi_r:       0.2492990670660760\nt_l: (  0  ) 0.0000000000000000\nt_r: (  0  ) 0.0000000000000000\nl: 0.0000000000000000\nFailure!\n\nMinimum density violation (eps):\nState: 0.8999999999000000 1.3999999999999999 3.0000000000000000\nSpecific entropy: 2.2148607362515862\nBounds: 0.9000000000000000 1.1000000000000001 2.0000000000000000\nBounds violation: low-order density (critical)!\n\t\trho min:         0.9000000000000000\n\t\trho min (delta): 0.0000000001000000\n\t\trho:             0.8999999999000000\n\t\trho max (delta): 0.0000000000000000\n\t\trho max:         1.1000000000000001\n\nBounds violation: high-order density!\n\t\trho min:         0.9000000000000000\n\t\trho min (delta): 0.0000000001000000\n\t\trho:             0.8999999999000000\n\t\trho max (delta): 0.0000000000000000\n\t\trho max:         1.1000000000000001\n\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.0000000000000000\nbreak: t_l and t_r within tolerance\npsi_l:       0.1668549449812342\npsi_r:       0.1668549449812342\nt_l: (  0  ) 0.0000000000000000\nt_r: (  0  ) 0.0000000000000000\nl: 0.0000000000000000\nFailure!\n\nMaximum density violation:\nState: 1.2000000000000000 1.3999999999999999 3.0000000000000000\nSpecific entropy: 1.6914777945209194\nBounds: 0.9000000000000000 1.1000000000000001 2.0000000000000000\nBounds violation: low-order density (critical)!\n\t\trho min:         0.9000000000000000\n\t\trho min (delta): 0.0000000000000000\n\t\trho:             1.2000000000000000\n\t\trho max (delta): 0.0999999999999999\n\t\trho max:         1.1000000000000001\n\nBounds violation: high-order density!\n\t\trho min:         0.9000000000000000\n\t\trho min (delta): 0.0000000000000000\n\t\trho:             1.2000000000000000\n\t\trho max (delta): 0.0999999999999999\n\t\trho max:         1.1000000000000001\n\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.0000000000000000\nBounds violation: low-order specific entropy (critical)!\n\t\tPsi left: 0 <= -0.4778828199657434\n\nbreak: t_l and t_r within tolerance\npsi_l:       -0.4778828199657434\npsi_r:       -0.4778828199657434\nt_l: (  0  ) 0.0000000000000000\nt_r: (  0  ) 0.0000000000000000\nBounds violation: high-order specific entropy!\n\t\trho e: 0 <= 2.1833333333333336\n\t\tPsi:   0 <= -0.4778828199657434\n\nl: 0.0000000000000000\nFailure!\n\nMaximum density violation (eps):\nState: 1.1000000001000001 1.3999999999999999 3.0000000000000000\nSpecific entropy: 1.8456338231106115\nBounds: 0.9000000000000000 1.1000000000000001 2.0000000000000000\nBounds violation: low-order density (critical)!\n\t\trho min:         0.9000000000000000\n\t\trho min (delta): 0.0000000000000000\n\t\trho:             1.1000000001000001\n\t\trho max (delta): 0.0000000001000000\n\t\trho max:         1.1000000000000001\n\nBounds violation: high-order density!\n\t\trho min:         0.9000000000000000\n\t\trho min (delta): 0.0000000000000000\n\t\trho:             1.1000000001000001\n\t\trho max (delta): 0.0000000001000000\n\t\trho max:         1.1000000000000001\n\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.0000000000000000\nBounds violation: low-order specific entropy (critical)!\n\t\tPsi left: 0 <= -0.1940414864233516\n\nbreak: t_l and t_r within tolerance\npsi_l:       -0.1940414864233516\npsi_r:       -0.1940414864233516\nt_l: (  0  ) 0.0000000000000000\nt_r: (  0  ) 0.0000000000000000\nBounds violation: high-order specific entropy!\n\t\trho e: 0 <= 2.1090909091719010\n\t\tPsi:   0 <= -0.1940414864233516\n\nl: 0.0000000000000000\nFailure!\n\nMinimum entropy violation:\nState: 1.0000000000000000 1.3999999999999999 2.7999999999999998\nSpecific entropy: 1.8199999999999998\nBounds: 0.9000000000000000 1.1000000000000001 2.0000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 1.0000000000000000\nBounds violation: low-order specific entropy (critical)!\n\t\tPsi left: 0 <= -0.1799999999999597\n\npsi_l:       -0.1799999999999597\npsi_r:       -0.6690414861748446\ndpsi_l:      -0.4400000000000000\ndpsi_r:      -0.5385181424381568\nt_l: (  0  ) 0.0000000000000000\nt_r: (  0  ) 0.0000000000000000\nbreak: t_l and t_r within tolerance\npsi_l:       -0.1799999999999597\npsi_r:       -0.1799999999999597\nt_l: (  1  ) 0.0000000000000000\nt_r: (  1  ) 0.0000000000000000\nBounds violation: high-order specific entropy!\n\t\trho e: 0 <= 1.8199999999999998\n\t\tPsi:   0 <= -0.1799999999999597\n\nl: 0.0000000000000000\nFailure!\n\nMinimum entropy violation (eps):\nState: 1.0000000000000000 1.3999999999999999 2.7999999999999998\nSpecific entropy: 1.8199999999999998\nBounds: 0.9000000000000000 1.1000000000000001 1.8200000001000001\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 1.0000000000000000\nBounds violation: low-order specific entropy (critical)!\n\t\tPsi left: 0 <= -0.0000000000999598\n\npsi_l:       -0.0000000000999598\npsi_r:       -0.3327777525448046\ndpsi_l:      -0.2968000000240001\ndpsi_r:      -0.3691515096461486\nt_l: (  0  ) 0.0000000000000000\nt_r: (  0  ) 0.0000000000000000\nbreak: t_l and t_r within tolerance\npsi_l:       -0.0000000000999598\npsi_r:       -0.0000000000999598\nt_l: (  1  ) 0.0000000000000000\nt_r: (  1  ) 0.0000000000000000\nBounds violation: high-order specific entropy!\n\t\trho e: 0 <= 1.8199999999999998\n\t\tPsi:   0 <= -0.0000000000999598\n\nl: 0.0000000000000000\nFailure!\n\n\nChecking individual limiter components:\n\nMinimum density bound\nState: 1.0000000000000000 1.3999999999999999 3.0000000000000000\nSpecific entropy: 2.0200000000000000\nBounds: 0.9000000000000000 1.1000000000000001 2.0000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.4999999999999993\nbreak: t_l and t_r within tolerance\npsi_l:       0.0200000000000449\npsi_r:       0.1406049448670616\nt_l: (  0  ) 0.4999999999999993\nt_r: (  0  ) 0.4999999999999993\nl: 0.4999999999999993\nSuccess!\n\nMinimum density bound (eps):\nState: 0.9000000001000000 1.3999999999999999 3.0000000000000000\nSpecific entropy: 2.2148607358429531\nBounds: 0.9000000000000000 1.1000000000000001 2.0000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.1999999188484877\nbreak: t_l and t_r within tolerance\npsi_l:       0.1668549447528901\npsi_r:       0.1566549490869405\nt_l: (  0  ) 0.1999999188484877\nt_r: (  0  ) 0.1999999188484877\nl: 0.1999999188484877\nSuccess!\n\nMaximum density bound\nState: 1.0000000000000000 1.3999999999999999 3.0000000000000000\nSpecific entropy: 2.0200000000000000\nBounds: 0.9000000000000000 1.1000000000000001 1.0000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.4999999999999998\nbreak: t_l and t_r within tolerance\npsi_l:       1.0200000000000449\npsi_r:       1.0467292569126085\nt_l: (  0  ) 0.4999999999999998\nt_r: (  0  ) 0.4999999999999998\nl: 0.4999999999999998\nSuccess!\n\nMaximum density bound (eps):\nState: 1.0999999999000001 1.3999999999999999 3.0000000000000000\nSpecific entropy: 1.8456338234386600\nBounds: 0.9000000000000000 1.1000000000000001 1.0000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.1999999188484877\nbreak: t_l and t_r within tolerance\npsi_l:       1.0629792568868683\npsi_r:       1.0567792595094574\nt_l: (  0  ) 0.1999999188484877\nt_r: (  0  ) 0.1999999188484877\nl: 0.1999999188484877\nSuccess!\n\nMinimum entropy bound\nState: 1.0000000000000000 1.3999999999999999 2.7999999999999998\nSpecific entropy: 1.8199999999999998\nBounds: 0.9000000000000000 1.1000000000000001 1.8000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 1.0000000000000000\npsi_l:       0.0200000000000402\npsi_r:       -0.6376373375573607\ndpsi_l:      -0.5920000000000001\ndpsi_r:      -0.7236663281943412\nt_l: (  0  ) 0.0336581777653587\nt_r: (  0  ) 0.0336795493578855\npsi_l:       0.0000004347665240\npsi_r:       -0.0000123111333648\ndpsi_l:      -0.5963930884367089\ndpsi_r:      -0.5963958787410997\nt_l: (  1  ) 0.0336589067585305\nt_r: (  1  ) 0.0336589067585308\nl: 0.0336589067585305\nSuccess!\n\nMinimum entropy bound (eps):\nState: 1.0000000000000000 1.3999999999999999 2.7999999999999998\nSpecific entropy: 1.8199999999999998\nBounds: 0.9000000000000000 1.1000000000000001 1.8199999999000001\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 1.0000000000000000\npsi_l:       0.0000000001000402\npsi_r:       -0.3327777527334004\ndpsi_l:      -0.2968000003760001\ndpsi_r:      -0.3691515100712968\nt_l: (  0  ) 0.0000000003370627\nt_r: (  0  ) 0.0000000003375128\nbreak: t_l and t_r within tolerance\npsi_l:       0.0000000000000000\npsi_r:       -0.0000000000001335\nt_l: (  1  ) 0.0000000003370627\nt_r: (  1  ) 0.0000000003375128\nl: 0.0000000003370627\nSuccess!\n"
  },
  {
    "path": "tests/euler/riemann_solver-iterated-10.cc",
    "content": "#define NEWTON_ITERATIONS \"10\"\n#include \"riemann_solver.cc\"\n"
  },
  {
    "path": "tests/euler/riemann_solver-iterated-10.threads=2.output",
    "content": "gamma: 1.3999999999999999e+00\n\n1.0000000000000000e+00 0.0000000000000000e+00 6.6666666666666666e-02\n1.0000000000000000e-03 0.0000000000000000e+00 6.6666666666666669e-11\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 6.6666666666666666e-02\na_left: 3.0550504633038933e-01\nrho_right: 1.0000000000000000e-03\nu_right: 0.0000000000000000e+00\np_right: 6.6666666666666669e-11\na_right: 3.0550504633038936e-04\np_star_two_rarefaction = 5.8723550288294769e-02\np_star_failsafe = 1.8962827388271815e-03\n   p^*_tilde  = 1.8962827388271815e-03\n   phi(p_*_t) = 6.4815741310316055e-01\np_1: (start) 0.0000000000666667\np_2: (start) 0.0018962827388272\ngap: (start) 1.5081835734444460\nl_m: (start) 1.5084890784907763\nphi_p_1:     -1.4484074490596666\nphi_p_2:     0.6481574131031606\ndphi_p_1:    172811373.9084264039993286\ndphi_p_2:    400.6612005604907267\np_1: (  0  ) 0.0000000084481396\np_2: (  0  ) 0.0010090151352667\ngap:         1.0971857559514193\nl_m:         1.1003718351781631\nphi_p_1:     -1.3668856648645886\nphi_p_2:     0.2288857236205296\ndphi_p_1:    2830289.5208312873728573\ndphi_p_2:    573.2396755409129128\np_1: (  1  ) 0.0000004916284500\np_2: (  1  ) 0.0007378813887158\ngap:         0.9166983626920455\nl_m:         0.9409876087347273\nphi_p_1:     -1.2249250918106023\nphi_p_2:     0.0593636949321878\ndphi_p_1:    102637.1916526903369231\ndphi_p_2:    686.7693222755584657\np_1: (  2  ) 0.0000126223171108\np_2: (  2  ) 0.0006630934193822\ngap:         0.7689546592423697\nl_m:         0.8920269707760770\nphi_p_1:     -0.9760505634677656\nphi_p_2:     0.0064011182691666\ndphi_p_1:    9143.4179408918171248\ndphi_p_2:    730.8421239823665019\np_1: (  3  ) 0.0001402891903397\np_2: (  3  ) 0.0006544571690915\ngap:         0.4758978112139954\nl_m:         0.8861989710234849\nphi_p_1:     -0.5523561576383821\nphi_p_2:     0.0000652401342803\ndphi_p_1:    1863.4608683972503513\ndphi_p_2:    736.4530131636109900\np_1: (  4  ) 0.0006542288067697\np_2: (  4  ) 0.0006543685891333\ngap:         0.0000946509497166\nl_m:         0.8861389960346452\nphi_p_1:     -0.0001029551105778\nphi_p_2:     0.0000000025808639\ndphi_p_1:    736.6030074943275849\ndphi_p_2:    736.5111847072535056\np_1: (  5  ) 0.0006543685856291\np_2: (  5  ) 0.0006543685856293\ngap:         0.0000000000001504\nl_m:         0.8861389936621376\nconverged after 6 iterations.\n-> lambda_max = 0.8861389936621376\n0.8861389936621376\n\n\n1.0000000000000000 0.0000000000000000 1.0000000000000000\n0.1250000000000000 0.0000000000000000 0.1000000000000000\nrho_left: 1.0000000000000000\nu_left: 0.0000000000000000\np_left: 1.0000000000000000\na_left: 1.1832159566199232\nrho_right: 0.1250000000000000\nu_right: 0.0000000000000000\np_right: 0.1000000000000000\na_right: 1.0583005244258361\np_star_two_rarefaction = 0.3067666466705971\np_star_failsafe = 0.3233302940900402\n   p^*_tilde  = 0.3067666466705971\n   phi(p_*_t) = 0.0197869634384757\np_1: (start) 0.1000000000000000\np_2: (start) 0.3067666466705971\ngap: (start) 0.7037890896510786\nl_m: (start) 1.7620896140769147\nphi_p_1:     -1.6583619228710595\nphi_p_2:     0.0197869634384757\ndphi_p_1:    13.6417435462253387\ndphi_p_2:    5.4159609146870071\np_1: (  0  ) 0.2996508642861503\np_2: (  0  ) 0.3031448223182616\ngap:         0.0095977036578061\nl_m:         1.7521958492860636\nphi_p_1:     -0.0191066324795303\nphi_p_2:     0.0000800549683353\ndphi_p_1:    5.5164242243628987\ndphi_p_2:    5.4665385875786940\np_1: (  1  ) 0.3031301775668962\np_2: (  1  ) 0.3031301780526657\ngap:         0.0000000013307568\nl_m:         1.7521557320357084\nphi_p_1:     -0.0000000026445413\nphi_p_2:     0.0000000000110366\ndphi_p_1:    5.4667454133152038\ndphi_p_2:    5.4667454064544376\np_1: (  2  ) 0.3031301780506468\np_2: (  2  ) 0.3031301780506470\ngap:         0.0000000000000007\nl_m:         1.7521557320301784\nconverged after 3 iterations.\n-> lambda_max = 1.7521557320301784\n1.7521557320301784\n\n\n0.4450000000000000 0.6980000000000000 3.5280000000000000\n0.5000000000000000 0.0000000000000000 0.5710000000000000\nrho_left: 0.4450000000000000\nu_left: 0.6980000000000000\np_left: 3.5280000000000000\na_left: 3.3315650740600322\nrho_right: 0.5000000000000000\nu_right: 0.0000000000000000\np_right: 0.5710000000000000\na_right: 1.2644366334458994\np_star_two_rarefaction = 2.5096631320093370\np_star_failsafe = 2.4778604535394879\n   p^*_tilde  = 2.4778604535394879\n   phi(p_*_t) = 0.0167309035684269\np_1: (start) 0.5710000000000000\np_2: (start) 2.4778604535394879\ngap: (start) 1.2205714326365371\nl_m: (start) 2.6335650740600323\nphi_p_1:     -4.5137957703718197\nphi_p_2:     0.0167309035684269\ndphi_p_1:    4.7946491786734651\ndphi_p_2:    1.4197066606631223\np_1: (  0  ) 2.3448943111953708\np_2: (  0  ) 2.4661243423755748\ngap:         0.0593866780990320\nl_m:         2.6335650740600323\nphi_p_1:     -0.1762015752045835\nphi_p_2:     0.0000376549674772\ndphi_p_1:    1.4833612567268535\ndphi_p_2:    1.4250678416566225\np_1: (  1  ) 2.4660979023049014\np_2: (  1  ) 2.4660979192109083\ngap:         0.0000000081825648\nl_m:         2.6335650740600323\nphi_p_1:     -0.0000000240873504\nphi_p_2:     0.0000000000050621\ndphi_p_1:    1.4250799733296207\ndphi_p_2:    1.4250799655724480\np_1: (  2  ) 2.4660979192073560\np_2: (  2  ) 2.4660979192073564\ngap:         0.0000000000000004\nl_m:         2.6335650740600323\nconverged after 3 iterations.\n-> lambda_max = 2.6335650740600323\n2.6335650740600323\n\n\n1.0000000000000000 10.0000000000000000 1000.0000000000000000\n1.0000000000000000 10.0000000000000000 0.0100000000000000\nrho_left: 1.0000000000000000\nu_left: 10.0000000000000000\np_left: 1000.0000000000000000\na_left: 37.4165738677394160\nrho_right: 1.0000000000000000\nu_right: 10.0000000000000000\np_right: 0.0100000000000000\na_right: 0.1183215956619923\np_star_two_rarefaction = 912.4493271057174297\np_star_failsafe = 480.7460990228586866\n   p^*_tilde  = 480.7460990228586866\n   phi(p_*_t) = 1.4297002883968268\np_1: (start) 0.0100000000000000\np_2: (start) 480.7460990228586866\ngap: (start) 23.9003652715968080\nl_m: (start) 34.0186868672588005\nphi_p_1:     -150.9628222441786818\nphi_p_2:     1.4297002883968268\ndphi_p_1:    524.4522153261194717\ndphi_p_2:    0.0708878624915873\np_1: (  0  ) 0.2980210301758181\np_2: (  0  ) 462.8822520334581441\ngap:         22.9685261997022891\nl_m:         33.5682138152247234\nphi_p_1:     -127.9408349322838774\nphi_p_2:     0.1452690429128403\ndphi_p_1:    28.9858850302915876\ndphi_p_2:    0.0729375753317012\np_1: (  1  ) 4.7544442813724634\np_2: (  1  ) 460.9139806437031552\ngap:         21.1290496063295379\nl_m:         33.5180521466477686\nphi_p_1:     -97.9622192286567497\nphi_p_2:     0.0014775995334482\ndphi_p_1:    2.8279485505887312\ndphi_p_2:    0.0731720894662831\np_1: (  2  ) 42.2419782114141640\np_2: (  2  ) 460.8937888875601061\ngap:         16.3976792895004451\nl_m:         33.5175370025237171\nphi_p_1:     -62.1061252761458036\nphi_p_2:     0.0000001021645275\ndphi_p_1:    0.4728415432655100\ndphi_p_2:    0.0731745045377525\np_1: (  3  ) 233.6359556728467055\np_2: (  3  ) 460.8937874913835344\ngap:         6.7734291433309082\nl_m:         33.5175369669032364\nphi_p_1:     -21.1365066810633522\nphi_p_2:     0.0000000000000107\ndphi_p_1:    0.1227995353942194\ndphi_p_2:    0.0731745047047512\np_1: (  4  ) 460.8937874913833639\np_2: (  4  ) 460.8937874913833639\ngap:         0.0000000000000000\nl_m:         33.5175369669032293\nconverged after 5 iterations.\n-> lambda_max = 33.5175369669032293\n33.5175369669032293\n\n\n5.9992400000000004 19.5975000000000001 460.8940000000000055\n5.9924200000000001 -6.1963299999999997 46.0949999999999989\nrho_left: 5.9992400000000004\nu_left: 19.5975000000000001\np_left: 460.8940000000000055\na_left: 10.3708995288366719\nrho_right: 5.9924200000000001\nu_right: -6.1963299999999997\np_right: 46.0949999999999989\na_right: 3.2816314493370298\np_star_two_rarefaction = 2322.6554570159960349\np_star_failsafe = 1759.9311105107933599\n   p^*_tilde  = 1759.9311105107933599\n   phi(p_*_t) = 0.7044512000385694\np_1: (start) 460.8940000000000055\np_2: (start) 1759.9311105107933599\ngap: (start) 9.1273008615823077\nl_m: (start) 12.6177579152028230\nphi_p_1:     -18.6479502573917379\nphi_p_2:     0.7044512000385694\ndphi_p_1:    0.0256748815655158\ndphi_p_2:    0.0101909374064949\np_1: (  0  ) 1625.3978322069895057\np_2: (  0  ) 1692.4259502711872756\ngap:         0.3673867836859408\nl_m:         12.2550058365779471\nphi_p_1:     -0.7006260339800221\nphi_p_2:     0.0081354029529805\ndphi_p_1:    0.0107094200774732\ndphi_p_2:    0.0104419616152000\np_1: (  1  ) 1691.6467221694349519\np_2: (  1  ) 1691.6469580629050142\ngap:         0.0000012803734126\nl_m:         12.2507781375426976\nphi_p_1:     -0.0000024360753166\nphi_p_2:     0.0000000278230701\ndphi_p_1:    0.0104449626862713\ndphi_p_2:    0.0104449617773948\np_1: (  2  ) 1691.6469553991260000\np_2: (  2  ) 1691.6469553991260000\ngap:         0.0000000000000000\nl_m:         12.2507781230843413\nconverged after 3 iterations.\n-> lambda_max = 12.2507781230843413\n12.2507781230843413\n\n\n1.0000000000000000 0.0000000000000000 0.0100000000000000\n1.0000000000000000 0.0000000000000000 100.0000000000000000\nrho_left: 1.0000000000000000\nu_left: 0.0000000000000000\np_left: 0.0100000000000000\na_left: 0.1183215956619923\nrho_right: 1.0000000000000000\nu_right: 0.0000000000000000\np_right: 100.0000000000000000\na_right: 11.8321595661992323\np_star_two_rarefaction = 82.9830692755807746\np_star_failsafe = 48.0794704355323148\n   p^*_tilde  = 48.0794704355323148\n   phi(p_*_t) = 0.4519168707738430\np_1: (start) 0.0100000000000000\np_2: (start) 48.0794704355323148\ngap: (start) 7.4775565314961021\nl_m: (start) 11.8321595661992323\nphi_p_1:     -43.2897554723354361\nphi_p_2:     0.4519168707738430\ndphi_p_1:    235.1807190995812391\ndphi_p_2:    0.2241634040272287\np_1: (  0  ) 0.1947777043485455\np_2: (  0  ) 46.2714967747562156\ngap:         6.9661738520101215\nl_m:         11.8321595661992323\nphi_p_1:     -34.5238798286377815\nphi_p_2:     0.0407707632033238\ndphi_p_1:    18.8814719214785143\ndphi_p_2:    0.2307258124711324\np_1: (  1  ) 2.0987828764434617\np_2: (  1  ) 46.0962900766029975\ngap:         5.8499560732148375\nl_m:         11.8321595661992323\nphi_p_1:     -23.7796034145985935\nphi_p_2:     0.0002882702579212\ndphi_p_1:    2.6354125550830645\ndphi_p_2:    0.2313861819354029\np_1: (  2  ) 13.4497611342039036\np_2: (  2  ) 46.0950442833053700\ngap:         3.4198011425704049\nl_m:         11.8321595661992323\nphi_p_1:     -11.3970585980925936\nphi_p_2:     0.0000000079685014\ndphi_p_1:    0.5963516404507745\ndphi_p_2:    0.2313908935100416\np_1: (  3  ) 46.0950442050942755\np_2: (  3  ) 46.0950442488679641\ngap:         0.0000000035313343\nl_m:         11.8321595661992323\nphi_p_1:     -0.0000000101288320\nphi_p_2:     -0.0000000000000009\ndphi_p_1:    0.2313908938058421\ndphi_p_2:    0.2313908936402865\np_1: (  4  ) 46.0950442488679641\np_2: (  4  ) 46.0950442488679641\ngap:         0.0000000000000000\nl_m:         11.8321595661992323\nconverged after 5 iterations.\n-> lambda_max = 11.8321595661992323\n11.8321595661992323\n\n\n1.0000000000000000 -1.0000000000000000 0.0100000000000000\n1.0000000000000000 -1.0000000000000000 100.0000000000000000\nrho_left: 1.0000000000000000\nu_left: -1.0000000000000000\np_left: 0.0100000000000000\na_left: 0.1183215956619923\nrho_right: 1.0000000000000000\nu_right: -1.0000000000000000\np_right: 100.0000000000000000\na_right: 11.8321595661992323\np_star_two_rarefaction = 82.9830692755807746\np_star_failsafe = 48.0794704355323148\n   p^*_tilde  = 48.0794704355323148\n   phi(p_*_t) = 0.4519168707738430\np_1: (start) 0.0100000000000000\np_2: (start) 48.0794704355323148\ngap: (start) 7.4775565314961021\nl_m: (start) 10.8321595661992323\nphi_p_1:     -43.2897554723354361\nphi_p_2:     0.4519168707738430\ndphi_p_1:    235.1807190995812391\ndphi_p_2:    0.2241634040272287\np_1: (  0  ) 0.1947777043485455\np_2: (  0  ) 46.2714967747562156\ngap:         6.9661738520101206\nl_m:         10.8321595661992323\nphi_p_1:     -34.5238798286377815\nphi_p_2:     0.0407707632033238\ndphi_p_1:    18.8814719214785143\ndphi_p_2:    0.2307258124711324\np_1: (  1  ) 2.0987828764434617\np_2: (  1  ) 46.0962900766029975\ngap:         5.8499560732148375\nl_m:         10.8321595661992323\nphi_p_1:     -23.7796034145985935\nphi_p_2:     0.0002882702579212\ndphi_p_1:    2.6354125550830645\ndphi_p_2:    0.2313861819354029\np_1: (  2  ) 13.4497611342039036\np_2: (  2  ) 46.0950442833053700\ngap:         3.4198011425704058\nl_m:         10.8321595661992323\nphi_p_1:     -11.3970585980925936\nphi_p_2:     0.0000000079685014\ndphi_p_1:    0.5963516404507745\ndphi_p_2:    0.2313908935100416\np_1: (  3  ) 46.0950442050942755\np_2: (  3  ) 46.0950442488679641\ngap:         0.0000000035313334\nl_m:         10.8321595661992323\nphi_p_1:     -0.0000000101288320\nphi_p_2:     -0.0000000000000009\ndphi_p_1:    0.2313908938058421\ndphi_p_2:    0.2313908936402865\np_1: (  4  ) 46.0950442488679641\np_2: (  4  ) 46.0950442488679641\ngap:         0.0000000000000000\nl_m:         10.8321595661992323\nconverged after 5 iterations.\n-> lambda_max = 10.8321595661992323\n10.8321595661992323\n\n\n1.0000000000000000 -2.1800000000000002 0.0100000000000000\n1.0000000000000000 -2.1800000000000002 100.0000000000000000\nrho_left: 1.0000000000000000\nu_left: -2.1800000000000002\np_left: 0.0100000000000000\na_left: 0.1183215956619923\nrho_right: 1.0000000000000000\nu_right: -2.1800000000000002\np_right: 100.0000000000000000\na_right: 11.8321595661992323\np_star_two_rarefaction = 82.9830692755807746\np_star_failsafe = 48.0794704355323148\n   p^*_tilde  = 48.0794704355323148\n   phi(p_*_t) = 0.4519168707738430\np_1: (start) 0.0100000000000000\np_2: (start) 48.0794704355323148\ngap: (start) 7.4775565314961021\nl_m: (start) 9.7758781271580943\nphi_p_1:     -43.2897554723354361\nphi_p_2:     0.4519168707738430\ndphi_p_1:    235.1807190995812391\ndphi_p_2:    0.2241634040272287\np_1: (  0  ) 0.1947777043485455\np_2: (  0  ) 46.2714967747562156\ngap:         6.9661738520101206\nl_m:         9.6521595661992325\nphi_p_1:     -34.5238798286377815\nphi_p_2:     0.0407707632033238\ndphi_p_1:    18.8814719214785143\ndphi_p_2:    0.2307258124711324\np_1: (  1  ) 2.0987828764434617\np_2: (  1  ) 46.0962900766029975\ngap:         5.8499560732148375\nl_m:         9.6521595661992325\nphi_p_1:     -23.7796034145985935\nphi_p_2:     0.0002882702579212\ndphi_p_1:    2.6354125550830645\ndphi_p_2:    0.2313861819354029\np_1: (  2  ) 13.4497611342039036\np_2: (  2  ) 46.0950442833053700\ngap:         3.4198011425704049\nl_m:         9.6521595661992325\nphi_p_1:     -11.3970585980925936\nphi_p_2:     0.0000000079685014\ndphi_p_1:    0.5963516404507745\ndphi_p_2:    0.2313908935100416\np_1: (  3  ) 46.0950442050942755\np_2: (  3  ) 46.0950442488679641\ngap:         0.0000000035313334\nl_m:         9.6521595661992325\nphi_p_1:     -0.0000000101288320\nphi_p_2:     -0.0000000000000009\ndphi_p_1:    0.2313908938058421\ndphi_p_2:    0.2313908936402865\np_1: (  4  ) 46.0950442488679641\np_2: (  4  ) 46.0950442488679641\ngap:         0.0000000000000000\nl_m:         9.6521595661992325\nconverged after 5 iterations.\n-> lambda_max = 9.6521595661992325\n9.6521595661992325\n\n\n0.0100000000000000 0.0000000000000000 0.0100000000000000\n1000.0000000000000000 0.0000000000000000 1000.0000000000000000\nrho_left: 0.0100000000000000\nu_left: 0.0000000000000000\np_left: 0.0100000000000000\na_left: 1.1832159566199232\nrho_right: 1000.0000000000000000\nu_right: 0.0000000000000000\np_right: 1000.0000000000000000\na_right: 1.1832159566199232\np_star_two_rarefaction = 0.3720052512407722\np_star_failsafe = 2.9291270455561289\n   p^*_tilde  = 0.3720052512407722\n   phi(p_*_t) = 1.4047089424401209\np_1: (start) 0.0100000000000000\np_2: (start) 0.3720052512407722\ngap: (start) 5.5130987125763093\nl_m: (start) 6.6963146691962327\nphi_p_1:     -4.7738636029873627\nphi_p_2:     1.4047089424401209\ndphi_p_1:    100.8327994744552711\ndphi_p_2:    8.4352208754268627\np_1: (  0  ) 0.0640478942977638\np_2: (  0  ) 0.2485574318703564\ngap:         2.6715226204805398\nl_m:         5.4796799016404929\nphi_p_1:     -2.5021698458717232\nphi_p_2:     0.2449817475004110\ndphi_p_1:    24.2880377765421827\ndphi_p_2:    10.5889463518251805\np_1: (  1  ) 0.2146200778035666\np_2: (  1  ) 0.2264932688770708\ngap:         0.1379659773924375\nl_m:         5.2325129971409039\nphi_p_1:     -0.1295712718749202\nphi_p_2:     0.0051067485026604\ndphi_p_1:    11.5220107666463285\ndphi_p_2:    11.1693097341533694\np_1: (  2  ) 0.2260361234669079\np_2: (  2  ) 0.2260363298521290\ngap:         0.0000023689448367\nl_m:         5.2272707584604294\nphi_p_1:     -0.0000022221461418\nphi_p_2:     0.0000000857186890\ndphi_p_1:    11.1823192834568026\ndphi_p_2:    11.1823134002654196\np_1: (  3  ) 0.2260363221865695\np_2: (  3  ) 0.2260363221865697\ngap:         0.0000000000000018\nl_m:         5.2272706704731062\nconverged after 4 iterations.\n-> lambda_max = 5.2272706704731062\n5.2272706704731062\n\n\n1.0000000000000000 2.1800000000000002 100.0000000000000000\n1.0000000000000000 2.1800000000000002 0.0100000000000000\nrho_left: 1.0000000000000000\nu_left: 2.1800000000000002\np_left: 100.0000000000000000\na_left: 11.8321595661992323\nrho_right: 1.0000000000000000\nu_right: 2.1800000000000002\np_right: 0.0100000000000000\na_right: 0.1183215956619923\np_star_two_rarefaction = 82.9830692755806609\np_star_failsafe = 48.0794704355323148\n   p^*_tilde  = 48.0794704355323148\n   phi(p_*_t) = 0.4519168707738430\np_1: (start) 0.0100000000000000\np_2: (start) 48.0794704355323148\ngap: (start) 7.4775565314961021\nl_m: (start) 9.7758781271580943\nphi_p_1:     -43.2897554723354361\nphi_p_2:     0.4519168707738430\ndphi_p_1:    235.1807190995812391\ndphi_p_2:    0.2241634040272287\np_1: (  0  ) 0.1947777043485455\np_2: (  0  ) 46.2714967747562156\ngap:         6.9661738520101206\nl_m:         9.6521595661992325\nphi_p_1:     -34.5238798286377815\nphi_p_2:     0.0407707632033238\ndphi_p_1:    18.8814719214785143\ndphi_p_2:    0.2307258124711324\np_1: (  1  ) 2.0987828764434617\np_2: (  1  ) 46.0962900766029975\ngap:         5.8499560732148375\nl_m:         9.6521595661992325\nphi_p_1:     -23.7796034145985935\nphi_p_2:     0.0002882702579212\ndphi_p_1:    2.6354125550830645\ndphi_p_2:    0.2313861819354029\np_1: (  2  ) 13.4497611342039036\np_2: (  2  ) 46.0950442833053700\ngap:         3.4198011425704049\nl_m:         9.6521595661992325\nphi_p_1:     -11.3970585980925936\nphi_p_2:     0.0000000079685014\ndphi_p_1:    0.5963516404507745\ndphi_p_2:    0.2313908935100416\np_1: (  3  ) 46.0950442050942755\np_2: (  3  ) 46.0950442488679641\ngap:         0.0000000035313334\nl_m:         9.6521595661992325\nphi_p_1:     -0.0000000101288320\nphi_p_2:     -0.0000000000000009\ndphi_p_1:    0.2313908938058421\ndphi_p_2:    0.2313908936402865\np_1: (  4  ) 46.0950442488679641\np_2: (  4  ) 46.0950442488679641\ngap:         0.0000000000000000\nl_m:         9.6521595661992325\nconverged after 5 iterations.\n-> lambda_max = 9.6521595661992325\n9.6521595661992325\n\n\n"
  },
  {
    "path": "tests/euler/riemann_solver-iterated-2.cc",
    "content": "#define NEWTON_ITERATIONS \"2\"\n#include \"riemann_solver.cc\"\n"
  },
  {
    "path": "tests/euler/riemann_solver-iterated-2.threads=2.output",
    "content": "gamma: 1.3999999999999999e+00\n\n1.0000000000000000e+00 0.0000000000000000e+00 6.6666666666666666e-02\n1.0000000000000000e-03 0.0000000000000000e+00 6.6666666666666669e-11\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 6.6666666666666666e-02\na_left: 3.0550504633038933e-01\nrho_right: 1.0000000000000000e-03\nu_right: 0.0000000000000000e+00\np_right: 6.6666666666666669e-11\na_right: 3.0550504633038936e-04\np_star_two_rarefaction = 5.8723550288294769e-02\np_star_failsafe = 1.8962827388271815e-03\n   p^*_tilde  = 1.8962827388271815e-03\n   phi(p_*_t) = 6.4815741310316055e-01\np_1: (start) 0.0000000000666667\np_2: (start) 0.0018962827388272\ngap: (start) 1.5081835734444460\nl_m: (start) 1.5084890784907763\nphi_p_1:     -1.4484074490596666\nphi_p_2:     0.6481574131031606\ndphi_p_1:    172811373.9084264039993286\ndphi_p_2:    400.6612005604907267\np_1: (  0  ) 0.0000000084481396\np_2: (  0  ) 0.0010090151352667\ngap:         1.0971857559514193\nl_m:         1.1003718351781631\nphi_p_1:     -1.3668856648645886\nphi_p_2:     0.2288857236205296\ndphi_p_1:    2830289.5208312873728573\ndphi_p_2:    573.2396755409129128\np_1: (  1  ) 0.0000004916284500\np_2: (  1  ) 0.0007378813887158\ngap:         0.9166983626920455\nl_m:         0.9409876087347273\n-> lambda_max = 0.9409876087347273\n0.9409876087347273\n\n\n1.0000000000000000 0.0000000000000000 1.0000000000000000\n0.1250000000000000 0.0000000000000000 0.1000000000000000\nrho_left: 1.0000000000000000\nu_left: 0.0000000000000000\np_left: 1.0000000000000000\na_left: 1.1832159566199232\nrho_right: 0.1250000000000000\nu_right: 0.0000000000000000\np_right: 0.1000000000000000\na_right: 1.0583005244258361\np_star_two_rarefaction = 0.3067666466705971\np_star_failsafe = 0.3233302940900402\n   p^*_tilde  = 0.3067666466705971\n   phi(p_*_t) = 0.0197869634384757\np_1: (start) 0.1000000000000000\np_2: (start) 0.3067666466705971\ngap: (start) 0.7037890896510786\nl_m: (start) 1.7620896140769147\nphi_p_1:     -1.6583619228710595\nphi_p_2:     0.0197869634384757\ndphi_p_1:    13.6417435462253387\ndphi_p_2:    5.4159609146870071\np_1: (  0  ) 0.2996508642861503\np_2: (  0  ) 0.3031448223182616\ngap:         0.0095977036578061\nl_m:         1.7521958492860636\nphi_p_1:     -0.0191066324795303\nphi_p_2:     0.0000800549683353\ndphi_p_1:    5.5164242243628987\ndphi_p_2:    5.4665385875786940\np_1: (  1  ) 0.3031301775668962\np_2: (  1  ) 0.3031301780526657\ngap:         0.0000000013307568\nl_m:         1.7521557320357084\n-> lambda_max = 1.7521557320357084\n1.7521557320357084\n\n\n0.4450000000000000 0.6980000000000000 3.5280000000000000\n0.5000000000000000 0.0000000000000000 0.5710000000000000\nrho_left: 0.4450000000000000\nu_left: 0.6980000000000000\np_left: 3.5280000000000000\na_left: 3.3315650740600322\nrho_right: 0.5000000000000000\nu_right: 0.0000000000000000\np_right: 0.5710000000000000\na_right: 1.2644366334458994\np_star_two_rarefaction = 2.5096631320093370\np_star_failsafe = 2.4778604535394879\n   p^*_tilde  = 2.4778604535394879\n   phi(p_*_t) = 0.0167309035684269\np_1: (start) 0.5710000000000000\np_2: (start) 2.4778604535394879\ngap: (start) 1.2205714326365371\nl_m: (start) 2.6335650740600323\nphi_p_1:     -4.5137957703718197\nphi_p_2:     0.0167309035684269\ndphi_p_1:    4.7946491786734651\ndphi_p_2:    1.4197066606631223\np_1: (  0  ) 2.3448943111953708\np_2: (  0  ) 2.4661243423755748\ngap:         0.0593866780990320\nl_m:         2.6335650740600323\nphi_p_1:     -0.1762015752045835\nphi_p_2:     0.0000376549674772\ndphi_p_1:    1.4833612567268535\ndphi_p_2:    1.4250678416566225\np_1: (  1  ) 2.4660979023049014\np_2: (  1  ) 2.4660979192109083\ngap:         0.0000000081825648\nl_m:         2.6335650740600323\n-> lambda_max = 2.6335650740600323\n2.6335650740600323\n\n\n1.0000000000000000 10.0000000000000000 1000.0000000000000000\n1.0000000000000000 10.0000000000000000 0.0100000000000000\nrho_left: 1.0000000000000000\nu_left: 10.0000000000000000\np_left: 1000.0000000000000000\na_left: 37.4165738677394160\nrho_right: 1.0000000000000000\nu_right: 10.0000000000000000\np_right: 0.0100000000000000\na_right: 0.1183215956619923\np_star_two_rarefaction = 912.4493271057174297\np_star_failsafe = 480.7460990228586866\n   p^*_tilde  = 480.7460990228586866\n   phi(p_*_t) = 1.4297002883968268\np_1: (start) 0.0100000000000000\np_2: (start) 480.7460990228586866\ngap: (start) 23.9003652715968080\nl_m: (start) 34.0186868672588005\nphi_p_1:     -150.9628222441786818\nphi_p_2:     1.4297002883968268\ndphi_p_1:    524.4522153261194717\ndphi_p_2:    0.0708878624915873\np_1: (  0  ) 0.2980210301758181\np_2: (  0  ) 462.8822520334581441\ngap:         22.9685261997022891\nl_m:         33.5682138152247234\nphi_p_1:     -127.9408349322838774\nphi_p_2:     0.1452690429128403\ndphi_p_1:    28.9858850302915876\ndphi_p_2:    0.0729375753317012\np_1: (  1  ) 4.7544442813724634\np_2: (  1  ) 460.9139806437031552\ngap:         21.1290496063295379\nl_m:         33.5180521466477686\n-> lambda_max = 33.5180521466477686\n33.5180521466477686\n\n\n5.9992400000000004 19.5975000000000001 460.8940000000000055\n5.9924200000000001 -6.1963299999999997 46.0949999999999989\nrho_left: 5.9992400000000004\nu_left: 19.5975000000000001\np_left: 460.8940000000000055\na_left: 10.3708995288366719\nrho_right: 5.9924200000000001\nu_right: -6.1963299999999997\np_right: 46.0949999999999989\na_right: 3.2816314493370298\np_star_two_rarefaction = 2322.6554570159960349\np_star_failsafe = 1759.9311105107933599\n   p^*_tilde  = 1759.9311105107933599\n   phi(p_*_t) = 0.7044512000385694\np_1: (start) 460.8940000000000055\np_2: (start) 1759.9311105107933599\ngap: (start) 9.1273008615823077\nl_m: (start) 12.6177579152028230\nphi_p_1:     -18.6479502573917379\nphi_p_2:     0.7044512000385694\ndphi_p_1:    0.0256748815655158\ndphi_p_2:    0.0101909374064949\np_1: (  0  ) 1625.3978322069895057\np_2: (  0  ) 1692.4259502711872756\ngap:         0.3673867836859408\nl_m:         12.2550058365779471\nphi_p_1:     -0.7006260339800221\nphi_p_2:     0.0081354029529805\ndphi_p_1:    0.0107094200774732\ndphi_p_2:    0.0104419616152000\np_1: (  1  ) 1691.6467221694349519\np_2: (  1  ) 1691.6469580629050142\ngap:         0.0000012803734126\nl_m:         12.2507781375426976\n-> lambda_max = 12.2507781375426976\n12.2507781375426976\n\n\n1.0000000000000000 0.0000000000000000 0.0100000000000000\n1.0000000000000000 0.0000000000000000 100.0000000000000000\nrho_left: 1.0000000000000000\nu_left: 0.0000000000000000\np_left: 0.0100000000000000\na_left: 0.1183215956619923\nrho_right: 1.0000000000000000\nu_right: 0.0000000000000000\np_right: 100.0000000000000000\na_right: 11.8321595661992323\np_star_two_rarefaction = 82.9830692755807746\np_star_failsafe = 48.0794704355323148\n   p^*_tilde  = 48.0794704355323148\n   phi(p_*_t) = 0.4519168707738430\np_1: (start) 0.0100000000000000\np_2: (start) 48.0794704355323148\ngap: (start) 7.4775565314961021\nl_m: (start) 11.8321595661992323\nphi_p_1:     -43.2897554723354361\nphi_p_2:     0.4519168707738430\ndphi_p_1:    235.1807190995812391\ndphi_p_2:    0.2241634040272287\np_1: (  0  ) 0.1947777043485455\np_2: (  0  ) 46.2714967747562156\ngap:         6.9661738520101215\nl_m:         11.8321595661992323\nphi_p_1:     -34.5238798286377815\nphi_p_2:     0.0407707632033238\ndphi_p_1:    18.8814719214785143\ndphi_p_2:    0.2307258124711324\np_1: (  1  ) 2.0987828764434617\np_2: (  1  ) 46.0962900766029975\ngap:         5.8499560732148375\nl_m:         11.8321595661992323\n-> lambda_max = 11.8321595661992323\n11.8321595661992323\n\n\n1.0000000000000000 -1.0000000000000000 0.0100000000000000\n1.0000000000000000 -1.0000000000000000 100.0000000000000000\nrho_left: 1.0000000000000000\nu_left: -1.0000000000000000\np_left: 0.0100000000000000\na_left: 0.1183215956619923\nrho_right: 1.0000000000000000\nu_right: -1.0000000000000000\np_right: 100.0000000000000000\na_right: 11.8321595661992323\np_star_two_rarefaction = 82.9830692755807746\np_star_failsafe = 48.0794704355323148\n   p^*_tilde  = 48.0794704355323148\n   phi(p_*_t) = 0.4519168707738430\np_1: (start) 0.0100000000000000\np_2: (start) 48.0794704355323148\ngap: (start) 7.4775565314961021\nl_m: (start) 10.8321595661992323\nphi_p_1:     -43.2897554723354361\nphi_p_2:     0.4519168707738430\ndphi_p_1:    235.1807190995812391\ndphi_p_2:    0.2241634040272287\np_1: (  0  ) 0.1947777043485455\np_2: (  0  ) 46.2714967747562156\ngap:         6.9661738520101206\nl_m:         10.8321595661992323\nphi_p_1:     -34.5238798286377815\nphi_p_2:     0.0407707632033238\ndphi_p_1:    18.8814719214785143\ndphi_p_2:    0.2307258124711324\np_1: (  1  ) 2.0987828764434617\np_2: (  1  ) 46.0962900766029975\ngap:         5.8499560732148375\nl_m:         10.8321595661992323\n-> lambda_max = 10.8321595661992323\n10.8321595661992323\n\n\n1.0000000000000000 -2.1800000000000002 0.0100000000000000\n1.0000000000000000 -2.1800000000000002 100.0000000000000000\nrho_left: 1.0000000000000000\nu_left: -2.1800000000000002\np_left: 0.0100000000000000\na_left: 0.1183215956619923\nrho_right: 1.0000000000000000\nu_right: -2.1800000000000002\np_right: 100.0000000000000000\na_right: 11.8321595661992323\np_star_two_rarefaction = 82.9830692755807746\np_star_failsafe = 48.0794704355323148\n   p^*_tilde  = 48.0794704355323148\n   phi(p_*_t) = 0.4519168707738430\np_1: (start) 0.0100000000000000\np_2: (start) 48.0794704355323148\ngap: (start) 7.4775565314961021\nl_m: (start) 9.7758781271580943\nphi_p_1:     -43.2897554723354361\nphi_p_2:     0.4519168707738430\ndphi_p_1:    235.1807190995812391\ndphi_p_2:    0.2241634040272287\np_1: (  0  ) 0.1947777043485455\np_2: (  0  ) 46.2714967747562156\ngap:         6.9661738520101206\nl_m:         9.6521595661992325\nphi_p_1:     -34.5238798286377815\nphi_p_2:     0.0407707632033238\ndphi_p_1:    18.8814719214785143\ndphi_p_2:    0.2307258124711324\np_1: (  1  ) 2.0987828764434617\np_2: (  1  ) 46.0962900766029975\ngap:         5.8499560732148375\nl_m:         9.6521595661992325\n-> lambda_max = 9.6521595661992325\n9.6521595661992325\n\n\n0.0100000000000000 0.0000000000000000 0.0100000000000000\n1000.0000000000000000 0.0000000000000000 1000.0000000000000000\nrho_left: 0.0100000000000000\nu_left: 0.0000000000000000\np_left: 0.0100000000000000\na_left: 1.1832159566199232\nrho_right: 1000.0000000000000000\nu_right: 0.0000000000000000\np_right: 1000.0000000000000000\na_right: 1.1832159566199232\np_star_two_rarefaction = 0.3720052512407722\np_star_failsafe = 2.9291270455561289\n   p^*_tilde  = 0.3720052512407722\n   phi(p_*_t) = 1.4047089424401209\np_1: (start) 0.0100000000000000\np_2: (start) 0.3720052512407722\ngap: (start) 5.5130987125763093\nl_m: (start) 6.6963146691962327\nphi_p_1:     -4.7738636029873627\nphi_p_2:     1.4047089424401209\ndphi_p_1:    100.8327994744552711\ndphi_p_2:    8.4352208754268627\np_1: (  0  ) 0.0640478942977638\np_2: (  0  ) 0.2485574318703564\ngap:         2.6715226204805398\nl_m:         5.4796799016404929\nphi_p_1:     -2.5021698458717232\nphi_p_2:     0.2449817475004110\ndphi_p_1:    24.2880377765421827\ndphi_p_2:    10.5889463518251805\np_1: (  1  ) 0.2146200778035666\np_2: (  1  ) 0.2264932688770708\ngap:         0.1379659773924375\nl_m:         5.2325129971409039\n-> lambda_max = 5.2325129971409039\n5.2325129971409039\n\n\n1.0000000000000000 2.1800000000000002 100.0000000000000000\n1.0000000000000000 2.1800000000000002 0.0100000000000000\nrho_left: 1.0000000000000000\nu_left: 2.1800000000000002\np_left: 100.0000000000000000\na_left: 11.8321595661992323\nrho_right: 1.0000000000000000\nu_right: 2.1800000000000002\np_right: 0.0100000000000000\na_right: 0.1183215956619923\np_star_two_rarefaction = 82.9830692755806609\np_star_failsafe = 48.0794704355323148\n   p^*_tilde  = 48.0794704355323148\n   phi(p_*_t) = 0.4519168707738430\np_1: (start) 0.0100000000000000\np_2: (start) 48.0794704355323148\ngap: (start) 7.4775565314961021\nl_m: (start) 9.7758781271580943\nphi_p_1:     -43.2897554723354361\nphi_p_2:     0.4519168707738430\ndphi_p_1:    235.1807190995812391\ndphi_p_2:    0.2241634040272287\np_1: (  0  ) 0.1947777043485455\np_2: (  0  ) 46.2714967747562156\ngap:         6.9661738520101206\nl_m:         9.6521595661992325\nphi_p_1:     -34.5238798286377815\nphi_p_2:     0.0407707632033238\ndphi_p_1:    18.8814719214785143\ndphi_p_2:    0.2307258124711324\np_1: (  1  ) 2.0987828764434617\np_2: (  1  ) 46.0962900766029975\ngap:         5.8499560732148375\nl_m:         9.6521595661992325\n-> lambda_max = 9.6521595661992325\n9.6521595661992325\n\n\n"
  },
  {
    "path": "tests/euler/riemann_solver-simd.cc",
    "content": "#define NUMBER VectorizedArray<double>\n#include \"riemann_solver.cc\"\n"
  },
  {
    "path": "tests/euler/riemann_solver-simd.threads=2.output",
    "content": "gamma: 1.3999999999999999e+00\n\n1.0000000000000000e+00 0.0000000000000000e+00 6.6666666666666666e-02\n1.0000000000000000e-03 0.0000000000000000e+00 6.6666666666666669e-11\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 6.6666666666666666e-02\na_left: 3.0550504633038933e-01\nrho_right: 1.0000000000000000e-03\nu_right: 0.0000000000000000e+00\np_right: 6.6666666666666669e-11\na_right: 3.0550504633038936e-04\np_star_two_rarefaction = 5.8723550288294769e-02\np_star_failsafe = 1.8962827388271815e-03\n   p^*_tilde  = 1.8962827388271815e-03\n   phi(p_*_t) = 6.4815741310316044e-01\n-> lambda_max = 1.5084890784907763e+00\n1.5084890784907763e+00\n\n\n1.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e+00\n1.2500000000000000e-01 0.0000000000000000e+00 1.0000000000000001e-01\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 1.0000000000000000e+00\na_left: 1.1832159566199232e+00\nrho_right: 1.2500000000000000e-01\nu_right: 0.0000000000000000e+00\np_right: 1.0000000000000001e-01\na_right: 1.0583005244258361e+00\np_star_two_rarefaction = 3.0676664667059700e-01\np_star_failsafe = 3.2333029409004022e-01\n   p^*_tilde  = 3.0676664667059700e-01\n   phi(p_*_t) = 1.9786963438475524e-02\n-> lambda_max = 1.7620896140769147e+00\n1.7620896140769147e+00\n\n\n4.4500000000000001e-01 6.9799999999999995e-01 3.5280000000000000e+00\n5.0000000000000000e-01 0.0000000000000000e+00 5.7099999999999995e-01\nrho_left: 4.4500000000000001e-01\nu_left: 6.9799999999999995e-01\np_left: 3.5280000000000000e+00\na_left: 3.3315650740600322e+00\nrho_right: 5.0000000000000000e-01\nu_right: 0.0000000000000000e+00\np_right: 5.7099999999999995e-01\na_right: 1.2644366334458994e+00\np_star_two_rarefaction = 2.5096631320093405e+00\np_star_failsafe = 2.4778604535394870e+00\n   p^*_tilde  = 2.4778604535394870e+00\n   phi(p_*_t) = 1.6730903568426436e-02\n-> lambda_max = 2.6335650740600323e+00\n2.6335650740600323e+00\n\n\n1.0000000000000000e+00 1.0000000000000000e+01 1.0000000000000000e+03\n1.0000000000000000e+00 1.0000000000000000e+01 1.0000000000000000e-02\nrho_left: 1.0000000000000000e+00\nu_left: 1.0000000000000000e+01\np_left: 1.0000000000000000e+03\na_left: 3.7416573867739416e+01\nrho_right: 1.0000000000000000e+00\nu_right: 1.0000000000000000e+01\np_right: 1.0000000000000000e-02\na_right: 1.1832159566199231e-01\np_star_two_rarefaction = 9.1244932710571743e+02\np_star_failsafe = 4.8074609902285869e+02\n   p^*_tilde  = 4.8074609902285869e+02\n   phi(p_*_t) = 1.4297002883968268e+00\n-> lambda_max = 3.4018686867258793e+01\n3.4018686867258793e+01\n\n\n5.9992400000000004e+00 1.9597500000000000e+01 4.6089400000000001e+02\n5.9924200000000001e+00 -6.1963299999999997e+00 4.6094999999999999e+01\nrho_left: 5.9992400000000004e+00\nu_left: 1.9597500000000000e+01\np_left: 4.6089400000000001e+02\na_left: 1.0370899528836672e+01\nrho_right: 5.9924200000000001e+00\nu_right: -6.1963299999999997e+00\np_right: 4.6094999999999999e+01\na_right: 3.2816314493370298e+00\np_star_two_rarefaction = 2.3226554570159965e+03\np_star_failsafe = 1.7599311105107940e+03\n   p^*_tilde  = 1.7599311105107940e+03\n   phi(p_*_t) = 7.0445120003857653e-01\n-> lambda_max = 1.2617757915202830e+01\n1.2617757915202830e+01\n\n\n1.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e-02\n1.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e+02\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 1.0000000000000000e-02\na_left: 1.1832159566199231e-01\nrho_right: 1.0000000000000000e+00\nu_right: 0.0000000000000000e+00\np_right: 1.0000000000000000e+02\na_right: 1.1832159566199232e+01\np_star_two_rarefaction = 8.2983069275580775e+01\np_star_failsafe = 4.8079470435532315e+01\n   p^*_tilde  = 4.8079470435532315e+01\n   phi(p_*_t) = 4.5191687077384302e-01\n-> lambda_max = 1.1832159566199232e+01\n1.1832159566199232e+01\n\n\n1.0000000000000000e+00 -1.0000000000000000e+00 1.0000000000000000e-02\n1.0000000000000000e+00 -1.0000000000000000e+00 1.0000000000000000e+02\nrho_left: 1.0000000000000000e+00\nu_left: -1.0000000000000000e+00\np_left: 1.0000000000000000e-02\na_left: 1.1832159566199231e-01\nrho_right: 1.0000000000000000e+00\nu_right: -1.0000000000000000e+00\np_right: 1.0000000000000000e+02\na_right: 1.1832159566199232e+01\np_star_two_rarefaction = 8.2983069275580775e+01\np_star_failsafe = 4.8079470435532315e+01\n   p^*_tilde  = 4.8079470435532315e+01\n   phi(p_*_t) = 4.5191687077384302e-01\n-> lambda_max = 1.0832159566199232e+01\n1.0832159566199232e+01\n\n\n1.0000000000000000e+00 -2.1800000000000002e+00 1.0000000000000000e-02\n1.0000000000000000e+00 -2.1800000000000002e+00 1.0000000000000000e+02\nrho_left: 1.0000000000000000e+00\nu_left: -2.1800000000000002e+00\np_left: 1.0000000000000000e-02\na_left: 1.1832159566199231e-01\nrho_right: 1.0000000000000000e+00\nu_right: -2.1800000000000002e+00\np_right: 1.0000000000000000e+02\na_right: 1.1832159566199232e+01\np_star_two_rarefaction = 8.2983069275580775e+01\np_star_failsafe = 4.8079470435532315e+01\n   p^*_tilde  = 4.8079470435532315e+01\n   phi(p_*_t) = 4.5191687077384302e-01\n-> lambda_max = 9.7758781271580943e+00\n9.7758781271580943e+00\n\n\n1.0000000000000000e-02 0.0000000000000000e+00 1.0000000000000000e-02\n1.0000000000000000e+03 0.0000000000000000e+00 1.0000000000000000e+03\nrho_left: 1.0000000000000000e-02\nu_left: 0.0000000000000000e+00\np_left: 1.0000000000000000e-02\na_left: 1.1832159566199232e+00\nrho_right: 1.0000000000000000e+03\nu_right: 0.0000000000000000e+00\np_right: 1.0000000000000000e+03\na_right: 1.1832159566199232e+00\np_star_two_rarefaction = 3.7200525124077216e-01\np_star_failsafe = 2.9291270455561289e+00\n   p^*_tilde  = 3.7200525124077216e-01\n   phi(p_*_t) = 1.4047089424401209e+00\n-> lambda_max = 6.6963146691962336e+00\n6.6963146691962336e+00\n\n\n1.0000000000000000e+00 2.1800000000000002e+00 1.0000000000000000e+02\n1.0000000000000000e+00 2.1800000000000002e+00 1.0000000000000000e-02\nrho_left: 1.0000000000000000e+00\nu_left: 2.1800000000000002e+00\np_left: 1.0000000000000000e+02\na_left: 1.1832159566199232e+01\nrho_right: 1.0000000000000000e+00\nu_right: 2.1800000000000002e+00\np_right: 1.0000000000000000e-02\na_right: 1.1832159566199231e-01\np_star_two_rarefaction = 8.2983069275580661e+01\np_star_failsafe = 4.8079470435532315e+01\n   p^*_tilde  = 4.8079470435532315e+01\n   phi(p_*_t) = 4.5191687077384302e-01\n-> lambda_max = 9.7758781271580943e+00\n9.7758781271580943e+00\n\n\n"
  },
  {
    "path": "tests/euler/riemann_solver-simd.threads=2.output.avx2",
    "content": "gamma: 1.3999999999999999e+00\n\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 6.6666666666666666e-02 6.6666666666666666e-02 6.6666666666666666e-02 6.6666666666666666e-02\n1.0000000000000000e-03 1.0000000000000000e-03 1.0000000000000000e-03 1.0000000000000000e-03 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 6.6666666666666669e-11 6.6666666666666669e-11 6.6666666666666669e-11 6.6666666666666669e-11\nrho_left: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00\np_left: 6.6666666666666666e-02 6.6666666666666666e-02 6.6666666666666666e-02 6.6666666666666666e-02\na_left: 3.0550504633038933e-01 3.0550504633038933e-01 3.0550504633038933e-01 3.0550504633038933e-01\nrho_right: 1.0000000000000000e-03 1.0000000000000000e-03 1.0000000000000000e-03 1.0000000000000000e-03\nu_right: 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00\np_right: 6.6666666666666669e-11 6.6666666666666669e-11 6.6666666666666669e-11 6.6666666666666669e-11\na_right: 3.0550504633038936e-04 3.0550504633038936e-04 3.0550504633038936e-04 3.0550504633038936e-04\np_star_two_rarefaction = 5.8723550288294769e-02 5.8723550288294769e-02 5.8723550288294769e-02 5.8723550288294769e-02\np_star_failsafe = 1.8962827388271815e-03 1.8962827388271815e-03 1.8962827388271815e-03 1.8962827388271815e-03\n   p^*_tilde  = 1.8962827388271815e-03 1.8962827388271815e-03 1.8962827388271815e-03 1.8962827388271815e-03\n   phi(p_*_t) = 6.4815741310316055e-01 6.4815741310316055e-01 6.4815741310316055e-01 6.4815741310316055e-01\n-> lambda_max = 1.5084890784907763e+00 1.5084890784907763e+00 1.5084890784907763e+00 1.5084890784907763e+00\n1.5084890784907763e+00 1.5084890784907763e+00 1.5084890784907763e+00 1.5084890784907763e+00\n\n\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\n1.2500000000000000e-01 1.2500000000000000e-01 1.2500000000000000e-01 1.2500000000000000e-01 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000001e-01 1.0000000000000001e-01 1.0000000000000001e-01 1.0000000000000001e-01\nrho_left: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00\np_left: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\na_left: 1.1832159566199232e+00 1.1832159566199232e+00 1.1832159566199232e+00 1.1832159566199232e+00\nrho_right: 1.2500000000000000e-01 1.2500000000000000e-01 1.2500000000000000e-01 1.2500000000000000e-01\nu_right: 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00\np_right: 1.0000000000000001e-01 1.0000000000000001e-01 1.0000000000000001e-01 1.0000000000000001e-01\na_right: 1.0583005244258361e+00 1.0583005244258361e+00 1.0583005244258361e+00 1.0583005244258361e+00\np_star_two_rarefaction = 3.0676664667059705e-01 3.0676664667059705e-01 3.0676664667059705e-01 3.0676664667059705e-01\np_star_failsafe = 3.2333029409004022e-01 3.2333029409004022e-01 3.2333029409004022e-01 3.2333029409004022e-01\n   p^*_tilde  = 3.0676664667059705e-01 3.0676664667059705e-01 3.0676664667059705e-01 3.0676664667059705e-01\n   phi(p_*_t) = 1.9786963438475746e-02 1.9786963438475746e-02 1.9786963438475746e-02 1.9786963438475746e-02\n-> lambda_max = 1.7620896140769147e+00 1.7620896140769147e+00 1.7620896140769147e+00 1.7620896140769147e+00\n1.7620896140769147e+00 1.7620896140769147e+00 1.7620896140769147e+00 1.7620896140769147e+00\n\n\n4.4500000000000001e-01 4.4500000000000001e-01 4.4500000000000001e-01 4.4500000000000001e-01 6.9799999999999995e-01 6.9799999999999995e-01 6.9799999999999995e-01 6.9799999999999995e-01 3.5280000000000000e+00 3.5280000000000000e+00 3.5280000000000000e+00 3.5280000000000000e+00\n5.0000000000000000e-01 5.0000000000000000e-01 5.0000000000000000e-01 5.0000000000000000e-01 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 5.7099999999999995e-01 5.7099999999999995e-01 5.7099999999999995e-01 5.7099999999999995e-01\nrho_left: 4.4500000000000001e-01 4.4500000000000001e-01 4.4500000000000001e-01 4.4500000000000001e-01\nu_left: 6.9799999999999995e-01 6.9799999999999995e-01 6.9799999999999995e-01 6.9799999999999995e-01\np_left: 3.5280000000000000e+00 3.5280000000000000e+00 3.5280000000000000e+00 3.5280000000000000e+00\na_left: 3.3315650740600322e+00 3.3315650740600322e+00 3.3315650740600322e+00 3.3315650740600322e+00\nrho_right: 5.0000000000000000e-01 5.0000000000000000e-01 5.0000000000000000e-01 5.0000000000000000e-01\nu_right: 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00\np_right: 5.7099999999999995e-01 5.7099999999999995e-01 5.7099999999999995e-01 5.7099999999999995e-01\na_right: 1.2644366334458994e+00 1.2644366334458994e+00 1.2644366334458994e+00 1.2644366334458994e+00\np_star_two_rarefaction = 2.5096631320093405e+00 2.5096631320093405e+00 2.5096631320093405e+00 2.5096631320093405e+00\np_star_failsafe = 2.4778604535394870e+00 2.4778604535394870e+00 2.4778604535394870e+00 2.4778604535394870e+00\n   p^*_tilde  = 2.4778604535394870e+00 2.4778604535394870e+00 2.4778604535394870e+00 2.4778604535394870e+00\n   phi(p_*_t) = 1.6730903568426436e-02 1.6730903568426436e-02 1.6730903568426436e-02 1.6730903568426436e-02\n-> lambda_max = 2.6335650740600323e+00 2.6335650740600323e+00 2.6335650740600323e+00 2.6335650740600323e+00\n2.6335650740600323e+00 2.6335650740600323e+00 2.6335650740600323e+00 2.6335650740600323e+00\n\n\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\nrho_left: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\nu_left: 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01\np_left: 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03\na_left: 3.7416573867739416e+01 3.7416573867739416e+01 3.7416573867739416e+01 3.7416573867739416e+01\nrho_right: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\nu_right: 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01\np_right: 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\na_right: 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01\np_star_two_rarefaction = 9.1244932710571743e+02 9.1244932710571743e+02 9.1244932710571743e+02 9.1244932710571743e+02\np_star_failsafe = 4.8074609902285869e+02 4.8074609902285869e+02 4.8074609902285869e+02 4.8074609902285869e+02\n   p^*_tilde  = 4.8074609902285869e+02 4.8074609902285869e+02 4.8074609902285869e+02 4.8074609902285869e+02\n   phi(p_*_t) = 1.4297002883968268e+00 1.4297002883968268e+00 1.4297002883968268e+00 1.4297002883968268e+00\n-> lambda_max = 3.4018686867258793e+01 3.4018686867258793e+01 3.4018686867258793e+01 3.4018686867258793e+01\n3.4018686867258793e+01 3.4018686867258793e+01 3.4018686867258793e+01 3.4018686867258793e+01\n\n\n5.9992400000000004e+00 5.9992400000000004e+00 5.9992400000000004e+00 5.9992400000000004e+00 1.9597500000000000e+01 1.9597500000000000e+01 1.9597500000000000e+01 1.9597500000000000e+01 4.6089400000000001e+02 4.6089400000000001e+02 4.6089400000000001e+02 4.6089400000000001e+02\n5.9924200000000001e+00 5.9924200000000001e+00 5.9924200000000001e+00 5.9924200000000001e+00 -6.1963299999999997e+00 -6.1963299999999997e+00 -6.1963299999999997e+00 -6.1963299999999997e+00 4.6094999999999999e+01 4.6094999999999999e+01 4.6094999999999999e+01 4.6094999999999999e+01\nrho_left: 5.9992400000000004e+00 5.9992400000000004e+00 5.9992400000000004e+00 5.9992400000000004e+00\nu_left: 1.9597500000000000e+01 1.9597500000000000e+01 1.9597500000000000e+01 1.9597500000000000e+01\np_left: 4.6089400000000001e+02 4.6089400000000001e+02 4.6089400000000001e+02 4.6089400000000001e+02\na_left: 1.0370899528836672e+01 1.0370899528836672e+01 1.0370899528836672e+01 1.0370899528836672e+01\nrho_right: 5.9924200000000001e+00 5.9924200000000001e+00 5.9924200000000001e+00 5.9924200000000001e+00\nu_right: -6.1963299999999997e+00 -6.1963299999999997e+00 -6.1963299999999997e+00 -6.1963299999999997e+00\np_right: 4.6094999999999999e+01 4.6094999999999999e+01 4.6094999999999999e+01 4.6094999999999999e+01\na_right: 3.2816314493370298e+00 3.2816314493370298e+00 3.2816314493370298e+00 3.2816314493370298e+00\np_star_two_rarefaction = 2.3226554570159960e+03 2.3226554570159960e+03 2.3226554570159960e+03 2.3226554570159960e+03\np_star_failsafe = 1.7599311105107940e+03 1.7599311105107940e+03 1.7599311105107940e+03 1.7599311105107940e+03\n   p^*_tilde  = 1.7599311105107940e+03 1.7599311105107940e+03 1.7599311105107940e+03 1.7599311105107940e+03\n   phi(p_*_t) = 7.0445120003857653e-01 7.0445120003857653e-01 7.0445120003857653e-01 7.0445120003857653e-01\n-> lambda_max = 1.2617757915202830e+01 1.2617757915202830e+01 1.2617757915202830e+01 1.2617757915202830e+01\n1.2617757915202830e+01 1.2617757915202830e+01 1.2617757915202830e+01 1.2617757915202830e+01\n\n\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02\nrho_left: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00\np_left: 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\na_left: 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01\nrho_right: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\nu_right: 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00\np_right: 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02\na_right: 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01\np_star_two_rarefaction = 8.2983069275580775e+01 8.2983069275580775e+01 8.2983069275580775e+01 8.2983069275580775e+01\np_star_failsafe = 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01\n   p^*_tilde  = 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01\n   phi(p_*_t) = 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01\n-> lambda_max = 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01\n1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01\n\n\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02\nrho_left: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\nu_left: -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00\np_left: 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\na_left: 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01\nrho_right: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\nu_right: -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00\np_right: 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02\na_right: 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01\np_star_two_rarefaction = 8.2983069275580775e+01 8.2983069275580775e+01 8.2983069275580775e+01 8.2983069275580775e+01\np_star_failsafe = 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01\n   p^*_tilde  = 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01\n   phi(p_*_t) = 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01\n-> lambda_max = 1.0832159566199232e+01 1.0832159566199232e+01 1.0832159566199232e+01 1.0832159566199232e+01\n1.0832159566199232e+01 1.0832159566199232e+01 1.0832159566199232e+01 1.0832159566199232e+01\n\n\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02\nrho_left: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\nu_left: -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00\np_left: 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\na_left: 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01\nrho_right: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\nu_right: -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00\np_right: 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02\na_right: 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01\np_star_two_rarefaction = 8.2983069275580775e+01 8.2983069275580775e+01 8.2983069275580775e+01 8.2983069275580775e+01\np_star_failsafe = 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01\n   p^*_tilde  = 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01\n   phi(p_*_t) = 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01\n-> lambda_max = 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00\n9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00\n\n\n1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\n1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03\nrho_left: 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\nu_left: 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00\np_left: 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\na_left: 1.1832159566199232e+00 1.1832159566199232e+00 1.1832159566199232e+00 1.1832159566199232e+00\nrho_right: 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03\nu_right: 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00\np_right: 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03\na_right: 1.1832159566199232e+00 1.1832159566199232e+00 1.1832159566199232e+00 1.1832159566199232e+00\np_star_two_rarefaction = 3.7200525124077216e-01 3.7200525124077216e-01 3.7200525124077216e-01 3.7200525124077216e-01\np_star_failsafe = 2.9291270455561289e+00 2.9291270455561289e+00 2.9291270455561289e+00 2.9291270455561289e+00\n   p^*_tilde  = 3.7200525124077216e-01 3.7200525124077216e-01 3.7200525124077216e-01 3.7200525124077216e-01\n   phi(p_*_t) = 1.4047089424401209e+00 1.4047089424401209e+00 1.4047089424401209e+00 1.4047089424401209e+00\n-> lambda_max = 6.6963146691962336e+00 6.6963146691962336e+00 6.6963146691962336e+00 6.6963146691962336e+00\n6.6963146691962336e+00 6.6963146691962336e+00 6.6963146691962336e+00 6.6963146691962336e+00\n\n\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\nrho_left: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\nu_left: 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00\np_left: 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02\na_left: 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01\nrho_right: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\nu_right: 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00\np_right: 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\na_right: 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01\np_star_two_rarefaction = 8.2983069275580661e+01 8.2983069275580661e+01 8.2983069275580661e+01 8.2983069275580661e+01\np_star_failsafe = 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01\n   p^*_tilde  = 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01\n   phi(p_*_t) = 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01\n-> lambda_max = 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00\n9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00\n\n\n"
  },
  {
    "path": "tests/euler/riemann_solver-simd.threads=2.output.avx512",
    "content": "gamma: 1.3999999999999999e+00\n\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 6.6666666666666666e-02 6.6666666666666666e-02 6.6666666666666666e-02 6.6666666666666666e-02 6.6666666666666666e-02 6.6666666666666666e-02 6.6666666666666666e-02 6.6666666666666666e-02\n1.0000000000000000e-03 1.0000000000000000e-03 1.0000000000000000e-03 1.0000000000000000e-03 1.0000000000000000e-03 1.0000000000000000e-03 1.0000000000000000e-03 1.0000000000000000e-03 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 6.6666666666666669e-11 6.6666666666666669e-11 6.6666666666666669e-11 6.6666666666666669e-11 6.6666666666666669e-11 6.6666666666666669e-11 6.6666666666666669e-11 6.6666666666666669e-11\nrho_left: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00\np_left: 6.6666666666666666e-02 6.6666666666666666e-02 6.6666666666666666e-02 6.6666666666666666e-02 6.6666666666666666e-02 6.6666666666666666e-02 6.6666666666666666e-02 6.6666666666666666e-02\na_left: 3.0550504633038933e-01 3.0550504633038933e-01 3.0550504633038933e-01 3.0550504633038933e-01 3.0550504633038933e-01 3.0550504633038933e-01 3.0550504633038933e-01 3.0550504633038933e-01\nrho_right: 1.0000000000000000e-03 1.0000000000000000e-03 1.0000000000000000e-03 1.0000000000000000e-03 1.0000000000000000e-03 1.0000000000000000e-03 1.0000000000000000e-03 1.0000000000000000e-03\nu_right: 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00\np_right: 6.6666666666666669e-11 6.6666666666666669e-11 6.6666666666666669e-11 6.6666666666666669e-11 6.6666666666666669e-11 6.6666666666666669e-11 6.6666666666666669e-11 6.6666666666666669e-11\na_right: 3.0550504633038936e-04 3.0550504633038936e-04 3.0550504633038936e-04 3.0550504633038936e-04 3.0550504633038936e-04 3.0550504633038936e-04 3.0550504633038936e-04 3.0550504633038936e-04\np_star_two_rarefaction = 5.8723550288294769e-02 5.8723550288294769e-02 5.8723550288294769e-02 5.8723550288294769e-02 5.8723550288294769e-02 5.8723550288294769e-02 5.8723550288294769e-02 5.8723550288294769e-02\np_star_failsafe = 1.8962827388271815e-03 1.8962827388271815e-03 1.8962827388271815e-03 1.8962827388271815e-03 1.8962827388271815e-03 1.8962827388271815e-03 1.8962827388271815e-03 1.8962827388271815e-03\n   p^*_tilde  = 1.8962827388271815e-03 1.8962827388271815e-03 1.8962827388271815e-03 1.8962827388271815e-03 1.8962827388271815e-03 1.8962827388271815e-03 1.8962827388271815e-03 1.8962827388271815e-03\n   phi(p_*_t) = 6.4815741310316055e-01 6.4815741310316055e-01 6.4815741310316055e-01 6.4815741310316055e-01 6.4815741310316055e-01 6.4815741310316055e-01 6.4815741310316055e-01 6.4815741310316055e-01\n-> lambda_max = 1.5084890784907763e+00 1.5084890784907763e+00 1.5084890784907763e+00 1.5084890784907763e+00 1.5084890784907763e+00 1.5084890784907763e+00 1.5084890784907763e+00 1.5084890784907763e+00\n1.5084890784907763e+00 1.5084890784907763e+00 1.5084890784907763e+00 1.5084890784907763e+00 1.5084890784907763e+00 1.5084890784907763e+00 1.5084890784907763e+00 1.5084890784907763e+00\n\n\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\n1.2500000000000000e-01 1.2500000000000000e-01 1.2500000000000000e-01 1.2500000000000000e-01 1.2500000000000000e-01 1.2500000000000000e-01 1.2500000000000000e-01 1.2500000000000000e-01 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000001e-01 1.0000000000000001e-01 1.0000000000000001e-01 1.0000000000000001e-01 1.0000000000000001e-01 1.0000000000000001e-01 1.0000000000000001e-01 1.0000000000000001e-01\nrho_left: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00\np_left: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\na_left: 1.1832159566199232e+00 1.1832159566199232e+00 1.1832159566199232e+00 1.1832159566199232e+00 1.1832159566199232e+00 1.1832159566199232e+00 1.1832159566199232e+00 1.1832159566199232e+00\nrho_right: 1.2500000000000000e-01 1.2500000000000000e-01 1.2500000000000000e-01 1.2500000000000000e-01 1.2500000000000000e-01 1.2500000000000000e-01 1.2500000000000000e-01 1.2500000000000000e-01\nu_right: 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00\np_right: 1.0000000000000001e-01 1.0000000000000001e-01 1.0000000000000001e-01 1.0000000000000001e-01 1.0000000000000001e-01 1.0000000000000001e-01 1.0000000000000001e-01 1.0000000000000001e-01\na_right: 1.0583005244258361e+00 1.0583005244258361e+00 1.0583005244258361e+00 1.0583005244258361e+00 1.0583005244258361e+00 1.0583005244258361e+00 1.0583005244258361e+00 1.0583005244258361e+00\np_star_two_rarefaction = 3.0676664667059705e-01 3.0676664667059705e-01 3.0676664667059705e-01 3.0676664667059705e-01 3.0676664667059705e-01 3.0676664667059705e-01 3.0676664667059705e-01 3.0676664667059705e-01\np_star_failsafe = 3.2333029409004022e-01 3.2333029409004022e-01 3.2333029409004022e-01 3.2333029409004022e-01 3.2333029409004022e-01 3.2333029409004022e-01 3.2333029409004022e-01 3.2333029409004022e-01\n   p^*_tilde  = 3.0676664667059705e-01 3.0676664667059705e-01 3.0676664667059705e-01 3.0676664667059705e-01 3.0676664667059705e-01 3.0676664667059705e-01 3.0676664667059705e-01 3.0676664667059705e-01\n   phi(p_*_t) = 1.9786963438475746e-02 1.9786963438475746e-02 1.9786963438475746e-02 1.9786963438475746e-02 1.9786963438475746e-02 1.9786963438475746e-02 1.9786963438475746e-02 1.9786963438475746e-02\n-> lambda_max = 1.7620896140769147e+00 1.7620896140769147e+00 1.7620896140769147e+00 1.7620896140769147e+00 1.7620896140769147e+00 1.7620896140769147e+00 1.7620896140769147e+00 1.7620896140769147e+00\n1.7620896140769147e+00 1.7620896140769147e+00 1.7620896140769147e+00 1.7620896140769147e+00 1.7620896140769147e+00 1.7620896140769147e+00 1.7620896140769147e+00 1.7620896140769147e+00\n\n\n4.4500000000000001e-01 4.4500000000000001e-01 4.4500000000000001e-01 4.4500000000000001e-01 4.4500000000000001e-01 4.4500000000000001e-01 4.4500000000000001e-01 4.4500000000000001e-01 6.9799999999999995e-01 6.9799999999999995e-01 6.9799999999999995e-01 6.9799999999999995e-01 6.9799999999999995e-01 6.9799999999999995e-01 6.9799999999999995e-01 6.9799999999999995e-01 3.5280000000000000e+00 3.5280000000000000e+00 3.5280000000000000e+00 3.5280000000000000e+00 3.5280000000000000e+00 3.5280000000000000e+00 3.5280000000000000e+00 3.5280000000000000e+00\n5.0000000000000000e-01 5.0000000000000000e-01 5.0000000000000000e-01 5.0000000000000000e-01 5.0000000000000000e-01 5.0000000000000000e-01 5.0000000000000000e-01 5.0000000000000000e-01 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 5.7099999999999995e-01 5.7099999999999995e-01 5.7099999999999995e-01 5.7099999999999995e-01 5.7099999999999995e-01 5.7099999999999995e-01 5.7099999999999995e-01 5.7099999999999995e-01\nrho_left: 4.4500000000000001e-01 4.4500000000000001e-01 4.4500000000000001e-01 4.4500000000000001e-01 4.4500000000000001e-01 4.4500000000000001e-01 4.4500000000000001e-01 4.4500000000000001e-01\nu_left: 6.9799999999999995e-01 6.9799999999999995e-01 6.9799999999999995e-01 6.9799999999999995e-01 6.9799999999999995e-01 6.9799999999999995e-01 6.9799999999999995e-01 6.9799999999999995e-01\np_left: 3.5280000000000000e+00 3.5280000000000000e+00 3.5280000000000000e+00 3.5280000000000000e+00 3.5280000000000000e+00 3.5280000000000000e+00 3.5280000000000000e+00 3.5280000000000000e+00\na_left: 3.3315650740600322e+00 3.3315650740600322e+00 3.3315650740600322e+00 3.3315650740600322e+00 3.3315650740600322e+00 3.3315650740600322e+00 3.3315650740600322e+00 3.3315650740600322e+00\nrho_right: 5.0000000000000000e-01 5.0000000000000000e-01 5.0000000000000000e-01 5.0000000000000000e-01 5.0000000000000000e-01 5.0000000000000000e-01 5.0000000000000000e-01 5.0000000000000000e-01\nu_right: 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00\np_right: 5.7099999999999995e-01 5.7099999999999995e-01 5.7099999999999995e-01 5.7099999999999995e-01 5.7099999999999995e-01 5.7099999999999995e-01 5.7099999999999995e-01 5.7099999999999995e-01\na_right: 1.2644366334458994e+00 1.2644366334458994e+00 1.2644366334458994e+00 1.2644366334458994e+00 1.2644366334458994e+00 1.2644366334458994e+00 1.2644366334458994e+00 1.2644366334458994e+00\np_star_two_rarefaction = 2.5096631320093405e+00 2.5096631320093405e+00 2.5096631320093405e+00 2.5096631320093405e+00 2.5096631320093405e+00 2.5096631320093405e+00 2.5096631320093405e+00 2.5096631320093405e+00\np_star_failsafe = 2.4778604535394870e+00 2.4778604535394870e+00 2.4778604535394870e+00 2.4778604535394870e+00 2.4778604535394870e+00 2.4778604535394870e+00 2.4778604535394870e+00 2.4778604535394870e+00\n   p^*_tilde  = 2.4778604535394870e+00 2.4778604535394870e+00 2.4778604535394870e+00 2.4778604535394870e+00 2.4778604535394870e+00 2.4778604535394870e+00 2.4778604535394870e+00 2.4778604535394870e+00\n   phi(p_*_t) = 1.6730903568426436e-02 1.6730903568426436e-02 1.6730903568426436e-02 1.6730903568426436e-02 1.6730903568426436e-02 1.6730903568426436e-02 1.6730903568426436e-02 1.6730903568426436e-02\n-> lambda_max = 2.6335650740600323e+00 2.6335650740600323e+00 2.6335650740600323e+00 2.6335650740600323e+00 2.6335650740600323e+00 2.6335650740600323e+00 2.6335650740600323e+00 2.6335650740600323e+00\n2.6335650740600323e+00 2.6335650740600323e+00 2.6335650740600323e+00 2.6335650740600323e+00 2.6335650740600323e+00 2.6335650740600323e+00 2.6335650740600323e+00 2.6335650740600323e+00\n\n\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\nrho_left: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\nu_left: 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01\np_left: 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03\na_left: 3.7416573867739416e+01 3.7416573867739416e+01 3.7416573867739416e+01 3.7416573867739416e+01 3.7416573867739416e+01 3.7416573867739416e+01 3.7416573867739416e+01 3.7416573867739416e+01\nrho_right: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\nu_right: 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+01\np_right: 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\na_right: 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01\np_star_two_rarefaction = 9.1244932710571743e+02 9.1244932710571743e+02 9.1244932710571743e+02 9.1244932710571743e+02 9.1244932710571743e+02 9.1244932710571743e+02 9.1244932710571743e+02 9.1244932710571743e+02\np_star_failsafe = 4.8074609902285869e+02 4.8074609902285869e+02 4.8074609902285869e+02 4.8074609902285869e+02 4.8074609902285869e+02 4.8074609902285869e+02 4.8074609902285869e+02 4.8074609902285869e+02\n   p^*_tilde  = 4.8074609902285869e+02 4.8074609902285869e+02 4.8074609902285869e+02 4.8074609902285869e+02 4.8074609902285869e+02 4.8074609902285869e+02 4.8074609902285869e+02 4.8074609902285869e+02\n   phi(p_*_t) = 1.4297002883968268e+00 1.4297002883968268e+00 1.4297002883968268e+00 1.4297002883968268e+00 1.4297002883968268e+00 1.4297002883968268e+00 1.4297002883968268e+00 1.4297002883968268e+00\n-> lambda_max = 3.4018686867258793e+01 3.4018686867258793e+01 3.4018686867258793e+01 3.4018686867258793e+01 3.4018686867258793e+01 3.4018686867258793e+01 3.4018686867258793e+01 3.4018686867258793e+01\n3.4018686867258793e+01 3.4018686867258793e+01 3.4018686867258793e+01 3.4018686867258793e+01 3.4018686867258793e+01 3.4018686867258793e+01 3.4018686867258793e+01 3.4018686867258793e+01\n\n\n5.9992400000000004e+00 5.9992400000000004e+00 5.9992400000000004e+00 5.9992400000000004e+00 5.9992400000000004e+00 5.9992400000000004e+00 5.9992400000000004e+00 5.9992400000000004e+00 1.9597500000000000e+01 1.9597500000000000e+01 1.9597500000000000e+01 1.9597500000000000e+01 1.9597500000000000e+01 1.9597500000000000e+01 1.9597500000000000e+01 1.9597500000000000e+01 4.6089400000000001e+02 4.6089400000000001e+02 4.6089400000000001e+02 4.6089400000000001e+02 4.6089400000000001e+02 4.6089400000000001e+02 4.6089400000000001e+02 4.6089400000000001e+02\n5.9924200000000001e+00 5.9924200000000001e+00 5.9924200000000001e+00 5.9924200000000001e+00 5.9924200000000001e+00 5.9924200000000001e+00 5.9924200000000001e+00 5.9924200000000001e+00 -6.1963299999999997e+00 -6.1963299999999997e+00 -6.1963299999999997e+00 -6.1963299999999997e+00 -6.1963299999999997e+00 -6.1963299999999997e+00 -6.1963299999999997e+00 -6.1963299999999997e+00 4.6094999999999999e+01 4.6094999999999999e+01 4.6094999999999999e+01 4.6094999999999999e+01 4.6094999999999999e+01 4.6094999999999999e+01 4.6094999999999999e+01 4.6094999999999999e+01\nrho_left: 5.9992400000000004e+00 5.9992400000000004e+00 5.9992400000000004e+00 5.9992400000000004e+00 5.9992400000000004e+00 5.9992400000000004e+00 5.9992400000000004e+00 5.9992400000000004e+00\nu_left: 1.9597500000000000e+01 1.9597500000000000e+01 1.9597500000000000e+01 1.9597500000000000e+01 1.9597500000000000e+01 1.9597500000000000e+01 1.9597500000000000e+01 1.9597500000000000e+01\np_left: 4.6089400000000001e+02 4.6089400000000001e+02 4.6089400000000001e+02 4.6089400000000001e+02 4.6089400000000001e+02 4.6089400000000001e+02 4.6089400000000001e+02 4.6089400000000001e+02\na_left: 1.0370899528836672e+01 1.0370899528836672e+01 1.0370899528836672e+01 1.0370899528836672e+01 1.0370899528836672e+01 1.0370899528836672e+01 1.0370899528836672e+01 1.0370899528836672e+01\nrho_right: 5.9924200000000001e+00 5.9924200000000001e+00 5.9924200000000001e+00 5.9924200000000001e+00 5.9924200000000001e+00 5.9924200000000001e+00 5.9924200000000001e+00 5.9924200000000001e+00\nu_right: -6.1963299999999997e+00 -6.1963299999999997e+00 -6.1963299999999997e+00 -6.1963299999999997e+00 -6.1963299999999997e+00 -6.1963299999999997e+00 -6.1963299999999997e+00 -6.1963299999999997e+00\np_right: 4.6094999999999999e+01 4.6094999999999999e+01 4.6094999999999999e+01 4.6094999999999999e+01 4.6094999999999999e+01 4.6094999999999999e+01 4.6094999999999999e+01 4.6094999999999999e+01\na_right: 3.2816314493370298e+00 3.2816314493370298e+00 3.2816314493370298e+00 3.2816314493370298e+00 3.2816314493370298e+00 3.2816314493370298e+00 3.2816314493370298e+00 3.2816314493370298e+00\np_star_two_rarefaction = 2.3226554570159960e+03 2.3226554570159960e+03 2.3226554570159960e+03 2.3226554570159960e+03 2.3226554570159960e+03 2.3226554570159960e+03 2.3226554570159960e+03 2.3226554570159960e+03\np_star_failsafe = 1.7599311105107940e+03 1.7599311105107940e+03 1.7599311105107940e+03 1.7599311105107940e+03 1.7599311105107940e+03 1.7599311105107940e+03 1.7599311105107940e+03 1.7599311105107940e+03\n   p^*_tilde  = 1.7599311105107940e+03 1.7599311105107940e+03 1.7599311105107940e+03 1.7599311105107940e+03 1.7599311105107940e+03 1.7599311105107940e+03 1.7599311105107940e+03 1.7599311105107940e+03\n   phi(p_*_t) = 7.0445120003857653e-01 7.0445120003857653e-01 7.0445120003857653e-01 7.0445120003857653e-01 7.0445120003857653e-01 7.0445120003857653e-01 7.0445120003857653e-01 7.0445120003857653e-01\n-> lambda_max = 1.2617757915202830e+01 1.2617757915202830e+01 1.2617757915202830e+01 1.2617757915202830e+01 1.2617757915202830e+01 1.2617757915202830e+01 1.2617757915202830e+01 1.2617757915202830e+01\n1.2617757915202830e+01 1.2617757915202830e+01 1.2617757915202830e+01 1.2617757915202830e+01 1.2617757915202830e+01 1.2617757915202830e+01 1.2617757915202830e+01 1.2617757915202830e+01\n\n\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02\nrho_left: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00\np_left: 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\na_left: 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01\nrho_right: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\nu_right: 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00\np_right: 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02\na_right: 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01\np_star_two_rarefaction = 8.2983069275580775e+01 8.2983069275580775e+01 8.2983069275580775e+01 8.2983069275580775e+01 8.2983069275580775e+01 8.2983069275580775e+01 8.2983069275580775e+01 8.2983069275580775e+01\np_star_failsafe = 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01\n   p^*_tilde  = 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01\n   phi(p_*_t) = 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01\n-> lambda_max = 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01\n1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01\n\n\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02\nrho_left: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\nu_left: -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00\np_left: 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\na_left: 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01\nrho_right: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\nu_right: -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00\np_right: 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02\na_right: 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01\np_star_two_rarefaction = 8.2983069275580775e+01 8.2983069275580775e+01 8.2983069275580775e+01 8.2983069275580775e+01 8.2983069275580775e+01 8.2983069275580775e+01 8.2983069275580775e+01 8.2983069275580775e+01\np_star_failsafe = 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01\n   p^*_tilde  = 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01\n   phi(p_*_t) = 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01\n-> lambda_max = 1.0832159566199232e+01 1.0832159566199232e+01 1.0832159566199232e+01 1.0832159566199232e+01 1.0832159566199232e+01 1.0832159566199232e+01 1.0832159566199232e+01 1.0832159566199232e+01\n1.0832159566199232e+01 1.0832159566199232e+01 1.0832159566199232e+01 1.0832159566199232e+01 1.0832159566199232e+01 1.0832159566199232e+01 1.0832159566199232e+01 1.0832159566199232e+01\n\n\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02\nrho_left: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\nu_left: -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00\np_left: 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\na_left: 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01\nrho_right: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\nu_right: -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 -2.1800000000000002e+00\np_right: 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02\na_right: 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01\np_star_two_rarefaction = 8.2983069275580775e+01 8.2983069275580775e+01 8.2983069275580775e+01 8.2983069275580775e+01 8.2983069275580775e+01 8.2983069275580775e+01 8.2983069275580775e+01 8.2983069275580775e+01\np_star_failsafe = 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01\n   p^*_tilde  = 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01\n   phi(p_*_t) = 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01\n-> lambda_max = 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00\n9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00\n\n\n1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\n1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03\nrho_left: 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\nu_left: 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00\np_left: 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\na_left: 1.1832159566199232e+00 1.1832159566199232e+00 1.1832159566199232e+00 1.1832159566199232e+00 1.1832159566199232e+00 1.1832159566199232e+00 1.1832159566199232e+00 1.1832159566199232e+00\nrho_right: 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03\nu_right: 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00\np_right: 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03 1.0000000000000000e+03\na_right: 1.1832159566199232e+00 1.1832159566199232e+00 1.1832159566199232e+00 1.1832159566199232e+00 1.1832159566199232e+00 1.1832159566199232e+00 1.1832159566199232e+00 1.1832159566199232e+00\np_star_two_rarefaction = 3.7200525124077216e-01 3.7200525124077216e-01 3.7200525124077216e-01 3.7200525124077216e-01 3.7200525124077216e-01 3.7200525124077216e-01 3.7200525124077216e-01 3.7200525124077216e-01\np_star_failsafe = 2.9291270455561289e+00 2.9291270455561289e+00 2.9291270455561289e+00 2.9291270455561289e+00 2.9291270455561289e+00 2.9291270455561289e+00 2.9291270455561289e+00 2.9291270455561289e+00\n   p^*_tilde  = 3.7200525124077216e-01 3.7200525124077216e-01 3.7200525124077216e-01 3.7200525124077216e-01 3.7200525124077216e-01 3.7200525124077216e-01 3.7200525124077216e-01 3.7200525124077216e-01\n   phi(p_*_t) = 1.4047089424401209e+00 1.4047089424401209e+00 1.4047089424401209e+00 1.4047089424401209e+00 1.4047089424401209e+00 1.4047089424401209e+00 1.4047089424401209e+00 1.4047089424401209e+00\n-> lambda_max = 6.6963146691962336e+00 6.6963146691962336e+00 6.6963146691962336e+00 6.6963146691962336e+00 6.6963146691962336e+00 6.6963146691962336e+00 6.6963146691962336e+00 6.6963146691962336e+00\n6.6963146691962336e+00 6.6963146691962336e+00 6.6963146691962336e+00 6.6963146691962336e+00 6.6963146691962336e+00 6.6963146691962336e+00 6.6963146691962336e+00 6.6963146691962336e+00\n\n\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\nrho_left: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\nu_left: 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00\np_left: 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02 1.0000000000000000e+02\na_left: 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01 1.1832159566199232e+01\nrho_right: 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\nu_right: 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00 2.1800000000000002e+00\np_right: 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02 1.0000000000000000e-02\na_right: 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01 1.1832159566199231e-01\np_star_two_rarefaction = 8.2983069275580661e+01 8.2983069275580661e+01 8.2983069275580661e+01 8.2983069275580661e+01 8.2983069275580661e+01 8.2983069275580661e+01 8.2983069275580661e+01 8.2983069275580661e+01\np_star_failsafe = 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01\n   p^*_tilde  = 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01 4.8079470435532315e+01\n   phi(p_*_t) = 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01 4.5191687077384302e-01\n-> lambda_max = 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00\n9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00 9.7758781271580943e+00\n\n\n"
  },
  {
    "path": "tests/euler/riemann_solver-simd.threads=2.output.sse2",
    "content": "gamma: 1.3999999999999999e+00\n\n1.0000000000000000e+00 1.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 6.6666666666666666e-02 6.6666666666666666e-02\n1.0000000000000000e-03 1.0000000000000000e-03 0.0000000000000000e+00 0.0000000000000000e+00 6.6666666666666669e-11 6.6666666666666669e-11\nrho_left: 1.0000000000000000e+00 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00 0.0000000000000000e+00\np_left: 6.6666666666666666e-02 6.6666666666666666e-02\na_left: 3.0550504633038933e-01 3.0550504633038933e-01\nrho_right: 1.0000000000000000e-03 1.0000000000000000e-03\nu_right: 0.0000000000000000e+00 0.0000000000000000e+00\np_right: 6.6666666666666669e-11 6.6666666666666669e-11\na_right: 3.0550504633038936e-04 3.0550504633038936e-04\np_star_two_rarefaction = 5.8723550288294769e-02 5.8723550288294769e-02\np_star_failsafe = 1.8962827388271815e-03 1.8962827388271815e-03\n   p^*_tilde  = 1.8962827388271815e-03 1.8962827388271815e-03\n   phi(p_*_t) = 6.4815741310316055e-01 6.4815741310316055e-01\n-> lambda_max = 1.5084890784907763e+00 1.5084890784907763e+00\n1.5084890784907763e+00 1.5084890784907763e+00\n\n\n1.0000000000000000e+00 1.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+00\n1.2500000000000000e-01 1.2500000000000000e-01 0.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000001e-01 1.0000000000000001e-01\nrho_left: 1.0000000000000000e+00 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00 0.0000000000000000e+00\np_left: 1.0000000000000000e+00 1.0000000000000000e+00\na_left: 1.1832159566199232e+00 1.1832159566199232e+00\nrho_right: 1.2500000000000000e-01 1.2500000000000000e-01\nu_right: 0.0000000000000000e+00 0.0000000000000000e+00\np_right: 1.0000000000000001e-01 1.0000000000000001e-01\na_right: 1.0583005244258361e+00 1.0583005244258361e+00\np_star_two_rarefaction = 3.0676664667059705e-01 3.0676664667059705e-01\np_star_failsafe = 3.2333029409004022e-01 3.2333029409004022e-01\n   p^*_tilde  = 3.0676664667059705e-01 3.0676664667059705e-01\n   phi(p_*_t) = 1.9786963438475746e-02 1.9786963438475746e-02\n-> lambda_max = 1.7620896140769147e+00 1.7620896140769147e+00\n1.7620896140769147e+00 1.7620896140769147e+00\n\n\n4.4500000000000001e-01 4.4500000000000001e-01 6.9799999999999995e-01 6.9799999999999995e-01 3.5280000000000000e+00 3.5280000000000000e+00\n5.0000000000000000e-01 5.0000000000000000e-01 0.0000000000000000e+00 0.0000000000000000e+00 5.7099999999999995e-01 5.7099999999999995e-01\nrho_left: 4.4500000000000001e-01 4.4500000000000001e-01\nu_left: 6.9799999999999995e-01 6.9799999999999995e-01\np_left: 3.5280000000000000e+00 3.5280000000000000e+00\na_left: 3.3315650740600322e+00 3.3315650740600322e+00\nrho_right: 5.0000000000000000e-01 5.0000000000000000e-01\nu_right: 0.0000000000000000e+00 0.0000000000000000e+00\np_right: 5.7099999999999995e-01 5.7099999999999995e-01\na_right: 1.2644366334458994e+00 1.2644366334458994e+00\np_star_two_rarefaction = 2.5096631320093405e+00 2.5096631320093405e+00\np_star_failsafe = 2.4778604535394870e+00 2.4778604535394870e+00\n   p^*_tilde  = 2.4778604535394870e+00 2.4778604535394870e+00\n   phi(p_*_t) = 1.6730903568426436e-02 1.6730903568426436e-02\n-> lambda_max = 2.6335650740600323e+00 2.6335650740600323e+00\n2.6335650740600323e+00 2.6335650740600323e+00\n\n\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e+03 1.0000000000000000e+03\n1.0000000000000000e+00 1.0000000000000000e+00 1.0000000000000000e+01 1.0000000000000000e+01 1.0000000000000000e-02 1.0000000000000000e-02\nrho_left: 1.0000000000000000e+00 1.0000000000000000e+00\nu_left: 1.0000000000000000e+01 1.0000000000000000e+01\np_left: 1.0000000000000000e+03 1.0000000000000000e+03\na_left: 3.7416573867739416e+01 3.7416573867739416e+01\nrho_right: 1.0000000000000000e+00 1.0000000000000000e+00\nu_right: 1.0000000000000000e+01 1.0000000000000000e+01\np_right: 1.0000000000000000e-02 1.0000000000000000e-02\na_right: 1.1832159566199231e-01 1.1832159566199231e-01\np_star_two_rarefaction = 9.1244932710571743e+02 9.1244932710571743e+02\np_star_failsafe = 4.8074609902285869e+02 4.8074609902285869e+02\n   p^*_tilde  = 4.8074609902285869e+02 4.8074609902285869e+02\n   phi(p_*_t) = 1.4297002883968268e+00 1.4297002883968268e+00\n-> lambda_max = 3.4018686867258793e+01 3.4018686867258793e+01\n3.4018686867258793e+01 3.4018686867258793e+01\n\n\n5.9992400000000004e+00 5.9992400000000004e+00 1.9597500000000000e+01 1.9597500000000000e+01 4.6089400000000001e+02 4.6089400000000001e+02\n5.9924200000000001e+00 5.9924200000000001e+00 -6.1963299999999997e+00 -6.1963299999999997e+00 4.6094999999999999e+01 4.6094999999999999e+01\nrho_left: 5.9992400000000004e+00 5.9992400000000004e+00\nu_left: 1.9597500000000000e+01 1.9597500000000000e+01\np_left: 4.6089400000000001e+02 4.6089400000000001e+02\na_left: 1.0370899528836672e+01 1.0370899528836672e+01\nrho_right: 5.9924200000000001e+00 5.9924200000000001e+00\nu_right: -6.1963299999999997e+00 -6.1963299999999997e+00\np_right: 4.6094999999999999e+01 4.6094999999999999e+01\na_right: 3.2816314493370298e+00 3.2816314493370298e+00\np_star_two_rarefaction = 2.3226554570159960e+03 2.3226554570159960e+03\np_star_failsafe = 1.7599311105107940e+03 1.7599311105107940e+03\n   p^*_tilde  = 1.7599311105107940e+03 1.7599311105107940e+03\n   phi(p_*_t) = 7.0445120003857653e-01 7.0445120003857653e-01\n-> lambda_max = 1.2617757915202830e+01 1.2617757915202830e+01\n1.2617757915202830e+01 1.2617757915202830e+01\n\n\n1.0000000000000000e+00 1.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e-02 1.0000000000000000e-02\n1.0000000000000000e+00 1.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e+02 1.0000000000000000e+02\nrho_left: 1.0000000000000000e+00 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00 0.0000000000000000e+00\np_left: 1.0000000000000000e-02 1.0000000000000000e-02\na_left: 1.1832159566199231e-01 1.1832159566199231e-01\nrho_right: 1.0000000000000000e+00 1.0000000000000000e+00\nu_right: 0.0000000000000000e+00 0.0000000000000000e+00\np_right: 1.0000000000000000e+02 1.0000000000000000e+02\na_right: 1.1832159566199232e+01 1.1832159566199232e+01\np_star_two_rarefaction = 8.2983069275580775e+01 8.2983069275580775e+01\np_star_failsafe = 4.8079470435532315e+01 4.8079470435532315e+01\n   p^*_tilde  = 4.8079470435532315e+01 4.8079470435532315e+01\n   phi(p_*_t) = 4.5191687077384302e-01 4.5191687077384302e-01\n-> lambda_max = 1.1832159566199232e+01 1.1832159566199232e+01\n1.1832159566199232e+01 1.1832159566199232e+01\n\n\n1.0000000000000000e+00 1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 1.0000000000000000e-02 1.0000000000000000e-02\n1.0000000000000000e+00 1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 1.0000000000000000e+02 1.0000000000000000e+02\nrho_left: 1.0000000000000000e+00 1.0000000000000000e+00\nu_left: -1.0000000000000000e+00 -1.0000000000000000e+00\np_left: 1.0000000000000000e-02 1.0000000000000000e-02\na_left: 1.1832159566199231e-01 1.1832159566199231e-01\nrho_right: 1.0000000000000000e+00 1.0000000000000000e+00\nu_right: -1.0000000000000000e+00 -1.0000000000000000e+00\np_right: 1.0000000000000000e+02 1.0000000000000000e+02\na_right: 1.1832159566199232e+01 1.1832159566199232e+01\np_star_two_rarefaction = 8.2983069275580775e+01 8.2983069275580775e+01\np_star_failsafe = 4.8079470435532315e+01 4.8079470435532315e+01\n   p^*_tilde  = 4.8079470435532315e+01 4.8079470435532315e+01\n   phi(p_*_t) = 4.5191687077384302e-01 4.5191687077384302e-01\n-> lambda_max = 1.0832159566199232e+01 1.0832159566199232e+01\n1.0832159566199232e+01 1.0832159566199232e+01\n\n\n1.0000000000000000e+00 1.0000000000000000e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 1.0000000000000000e-02 1.0000000000000000e-02\n1.0000000000000000e+00 1.0000000000000000e+00 -2.1800000000000002e+00 -2.1800000000000002e+00 1.0000000000000000e+02 1.0000000000000000e+02\nrho_left: 1.0000000000000000e+00 1.0000000000000000e+00\nu_left: -2.1800000000000002e+00 -2.1800000000000002e+00\np_left: 1.0000000000000000e-02 1.0000000000000000e-02\na_left: 1.1832159566199231e-01 1.1832159566199231e-01\nrho_right: 1.0000000000000000e+00 1.0000000000000000e+00\nu_right: -2.1800000000000002e+00 -2.1800000000000002e+00\np_right: 1.0000000000000000e+02 1.0000000000000000e+02\na_right: 1.1832159566199232e+01 1.1832159566199232e+01\np_star_two_rarefaction = 8.2983069275580775e+01 8.2983069275580775e+01\np_star_failsafe = 4.8079470435532315e+01 4.8079470435532315e+01\n   p^*_tilde  = 4.8079470435532315e+01 4.8079470435532315e+01\n   phi(p_*_t) = 4.5191687077384302e-01 4.5191687077384302e-01\n-> lambda_max = 9.7758781271580943e+00 9.7758781271580943e+00\n9.7758781271580943e+00 9.7758781271580943e+00\n\n\n1.0000000000000000e-02 1.0000000000000000e-02 0.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e-02 1.0000000000000000e-02\n1.0000000000000000e+03 1.0000000000000000e+03 0.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e+03 1.0000000000000000e+03\nrho_left: 1.0000000000000000e-02 1.0000000000000000e-02\nu_left: 0.0000000000000000e+00 0.0000000000000000e+00\np_left: 1.0000000000000000e-02 1.0000000000000000e-02\na_left: 1.1832159566199232e+00 1.1832159566199232e+00\nrho_right: 1.0000000000000000e+03 1.0000000000000000e+03\nu_right: 0.0000000000000000e+00 0.0000000000000000e+00\np_right: 1.0000000000000000e+03 1.0000000000000000e+03\na_right: 1.1832159566199232e+00 1.1832159566199232e+00\np_star_two_rarefaction = 3.7200525124077216e-01 3.7200525124077216e-01\np_star_failsafe = 2.9291270455561289e+00 2.9291270455561289e+00\n   p^*_tilde  = 3.7200525124077216e-01 3.7200525124077216e-01\n   phi(p_*_t) = 1.4047089424401209e+00 1.4047089424401209e+00\n-> lambda_max = 6.6963146691962336e+00 6.6963146691962336e+00\n6.6963146691962336e+00 6.6963146691962336e+00\n\n\n1.0000000000000000e+00 1.0000000000000000e+00 2.1800000000000002e+00 2.1800000000000002e+00 1.0000000000000000e+02 1.0000000000000000e+02\n1.0000000000000000e+00 1.0000000000000000e+00 2.1800000000000002e+00 2.1800000000000002e+00 1.0000000000000000e-02 1.0000000000000000e-02\nrho_left: 1.0000000000000000e+00 1.0000000000000000e+00\nu_left: 2.1800000000000002e+00 2.1800000000000002e+00\np_left: 1.0000000000000000e+02 1.0000000000000000e+02\na_left: 1.1832159566199232e+01 1.1832159566199232e+01\nrho_right: 1.0000000000000000e+00 1.0000000000000000e+00\nu_right: 2.1800000000000002e+00 2.1800000000000002e+00\np_right: 1.0000000000000000e-02 1.0000000000000000e-02\na_right: 1.1832159566199231e-01 1.1832159566199231e-01\np_star_two_rarefaction = 8.2983069275580661e+01 8.2983069275580661e+01\np_star_failsafe = 4.8079470435532315e+01 4.8079470435532315e+01\n   p^*_tilde  = 4.8079470435532315e+01 4.8079470435532315e+01\n   phi(p_*_t) = 4.5191687077384302e-01 4.5191687077384302e-01\n-> lambda_max = 9.7758781271580943e+00 9.7758781271580943e+00\n9.7758781271580943e+00 9.7758781271580943e+00\n\n\n"
  },
  {
    "path": "tests/euler/riemann_solver.cc",
    "content": "// force distinct symbols in test\n#define Euler EulerTest\n\n#include <hyperbolic_system.h>\n#include <multicomponent_vector.h>\n#define DEBUG_RIEMANN_SOLVER\n#include <riemann_solver.h>\n#include <riemann_solver.template.h>\n#include <simd.h>\n\n#ifndef NEWTON_ITERATIONS\n#define NEWTON_ITERATIONS \"0\"\n#endif\n\n#ifndef NUMBER\n#define NUMBER double\n#endif\n\nusing namespace ryujin::Euler;\nusing namespace ryujin;\nusing namespace dealii;\n\nint main()\n{\n  constexpr int dim = 1;\n  using Number = NUMBER;\n\n  HyperbolicSystem hyperbolic_system;\n  RiemannSolver<dim, Number>::Parameters riemann_solver_parameters;\n\n  const auto gamma = hyperbolic_system.view<dim, Number>().gamma();\n\n  static constexpr unsigned int n_precomputed_values =\n      HyperbolicSystemView<dim, Number>::n_precomputed_values;\n  using precomputed_type =\n      Vectors::MultiComponentVector<double, n_precomputed_values>;\n  precomputed_type dummy;\n\n  RiemannSolver<dim> riemann_solver(\n      hyperbolic_system, riemann_solver_parameters, dummy);\n\n  std::stringstream parameters;\n  parameters << \"subsection RiemannSolver\\n\"\n             << \"set newton max iterations = \" NEWTON_ITERATIONS \"\\n\"\n             << \"end\" << std::endl;\n  ParameterAcceptor::initialize(parameters);\n\n  const auto riemann_data = [&](const auto &state) {\n    const Number rho = state[0];\n    const Number u = state[1];\n    const Number p = state[2];\n\n    std::array<Number, 4> result;\n    result[0] = rho;\n    result[1] = u;\n    result[2] = p;\n    result[3] = std::sqrt(gamma * p / rho);\n    return result;\n  };\n\n  const auto test = [&](const std::array<Number, 3> &U_i,\n                        const std::array<Number, 3> &U_j) {\n    std::cout << U_i[0] << \" \" << U_i[1] << \" \" << U_i[2] << std::endl;\n    std::cout << U_j[0] << \" \" << U_j[1] << \" \" << U_j[2] << std::endl;\n    const auto rd_i = riemann_data(U_i);\n    const auto rd_j = riemann_data(U_j);\n    const auto lambda_max = riemann_solver.compute(rd_i, rd_j);\n    std::cout << lambda_max << std::endl;\n    std::cout << std::endl;\n    std::cout << std::endl;\n  };\n\n  std::cout << std::setprecision(16);\n  std::cout << std::scientific;\n\n  std::cout << \"gamma: \" << gamma << std::endl;\n  std::cout << std::endl;\n\n  /* Leblanc:*/\n  test({1., 0., 2. / 30.}, {1.e-3, 0., 2. / 3. * 1.e-10});\n  /* Sod:*/\n  test({1., 0., 1.}, {0.125, 0., 0.1});\n  /* Lax:*/\n  test({0.445, 0.698, 3.528}, {0.5, 0., 0.571});\n  /* Fast shock case 1 (paper, section 5.2): */\n  test({1., 1.e1, 1.e3}, {1., 10., 0.01});\n  /* Fast shock case 2 (paper, section 5.2): */\n  test({5.99924, 19.5975, 460.894}, {5.99242, -6.19633, 46.0950});\n  /* Fast expansion and slow shock, case 1 (Paper, section 5.1) */\n  test({1., 0., 0.01}, {1., 0., 1.e2});\n  /* Fast expansion and slow shock, case 2 (Paper, section 5.1) */\n  test({1., -1., 0.01}, {1., -1., 1.e2});\n  /* Fast expansion and slow shock, case 3 (Paper, section 5.1) */\n  test({1., -2.18, 0.01}, {1., -2.18, 100.});\n  /* Case 9:*/\n  test({1.0e-2, 0., 1.0e-2}, {1.e3, 0., 1.e3});\n  /* Case 10:*/\n  test({1.0, 2.18, 1.e2}, {1.0, 2.18, 0.01});\n\n  return 0;\n}\n"
  },
  {
    "path": "tests/euler/riemann_solver.threads=2.output",
    "content": "gamma: 1.3999999999999999e+00\n\n1.0000000000000000e+00 0.0000000000000000e+00 6.6666666666666666e-02\n1.0000000000000000e-03 0.0000000000000000e+00 6.6666666666666669e-11\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 6.6666666666666666e-02\na_left: 3.0550504633038933e-01\nrho_right: 1.0000000000000000e-03\nu_right: 0.0000000000000000e+00\np_right: 6.6666666666666669e-11\na_right: 3.0550504633038936e-04\np_star_two_rarefaction = 5.8723550288294769e-02\np_star_failsafe = 1.8962827388271815e-03\n   p^*_tilde  = 1.8962827388271815e-03\n   phi(p_*_t) = 6.4815741310316055e-01\n-> lambda_max = 1.5084890784907763e+00\n1.5084890784907763e+00\n\n\n1.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e+00\n1.2500000000000000e-01 0.0000000000000000e+00 1.0000000000000001e-01\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 1.0000000000000000e+00\na_left: 1.1832159566199232e+00\nrho_right: 1.2500000000000000e-01\nu_right: 0.0000000000000000e+00\np_right: 1.0000000000000001e-01\na_right: 1.0583005244258361e+00\np_star_two_rarefaction = 3.0676664667059705e-01\np_star_failsafe = 3.2333029409004022e-01\n   p^*_tilde  = 3.0676664667059705e-01\n   phi(p_*_t) = 1.9786963438475746e-02\n-> lambda_max = 1.7620896140769147e+00\n1.7620896140769147e+00\n\n\n4.4500000000000001e-01 6.9799999999999995e-01 3.5280000000000000e+00\n5.0000000000000000e-01 0.0000000000000000e+00 5.7099999999999995e-01\nrho_left: 4.4500000000000001e-01\nu_left: 6.9799999999999995e-01\np_left: 3.5280000000000000e+00\na_left: 3.3315650740600322e+00\nrho_right: 5.0000000000000000e-01\nu_right: 0.0000000000000000e+00\np_right: 5.7099999999999995e-01\na_right: 1.2644366334458994e+00\np_star_two_rarefaction = 2.5096631320093370e+00\np_star_failsafe = 2.4778604535394879e+00\n   p^*_tilde  = 2.4778604535394879e+00\n   phi(p_*_t) = 1.6730903568426880e-02\n-> lambda_max = 2.6335650740600323e+00\n2.6335650740600323e+00\n\n\n1.0000000000000000e+00 1.0000000000000000e+01 1.0000000000000000e+03\n1.0000000000000000e+00 1.0000000000000000e+01 1.0000000000000000e-02\nrho_left: 1.0000000000000000e+00\nu_left: 1.0000000000000000e+01\np_left: 1.0000000000000000e+03\na_left: 3.7416573867739416e+01\nrho_right: 1.0000000000000000e+00\nu_right: 1.0000000000000000e+01\np_right: 1.0000000000000000e-02\na_right: 1.1832159566199231e-01\np_star_two_rarefaction = 9.1244932710571743e+02\np_star_failsafe = 4.8074609902285869e+02\n   p^*_tilde  = 4.8074609902285869e+02\n   phi(p_*_t) = 1.4297002883968268e+00\n-> lambda_max = 3.4018686867258801e+01\n3.4018686867258801e+01\n\n\n5.9992400000000004e+00 1.9597500000000000e+01 4.6089400000000001e+02\n5.9924200000000001e+00 -6.1963299999999997e+00 4.6094999999999999e+01\nrho_left: 5.9992400000000004e+00\nu_left: 1.9597500000000000e+01\np_left: 4.6089400000000001e+02\na_left: 1.0370899528836672e+01\nrho_right: 5.9924200000000001e+00\nu_right: -6.1963299999999997e+00\np_right: 4.6094999999999999e+01\na_right: 3.2816314493370298e+00\np_star_two_rarefaction = 2.3226554570159960e+03\np_star_failsafe = 1.7599311105107934e+03\n   p^*_tilde  = 1.7599311105107934e+03\n   phi(p_*_t) = 7.0445120003856943e-01\n-> lambda_max = 1.2617757915202823e+01\n1.2617757915202823e+01\n\n\n1.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e-02\n1.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e+02\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 1.0000000000000000e-02\na_left: 1.1832159566199231e-01\nrho_right: 1.0000000000000000e+00\nu_right: 0.0000000000000000e+00\np_right: 1.0000000000000000e+02\na_right: 1.1832159566199232e+01\np_star_two_rarefaction = 8.2983069275580775e+01\np_star_failsafe = 4.8079470435532315e+01\n   p^*_tilde  = 4.8079470435532315e+01\n   phi(p_*_t) = 4.5191687077384302e-01\n-> lambda_max = 1.1832159566199232e+01\n1.1832159566199232e+01\n\n\n1.0000000000000000e+00 -1.0000000000000000e+00 1.0000000000000000e-02\n1.0000000000000000e+00 -1.0000000000000000e+00 1.0000000000000000e+02\nrho_left: 1.0000000000000000e+00\nu_left: -1.0000000000000000e+00\np_left: 1.0000000000000000e-02\na_left: 1.1832159566199231e-01\nrho_right: 1.0000000000000000e+00\nu_right: -1.0000000000000000e+00\np_right: 1.0000000000000000e+02\na_right: 1.1832159566199232e+01\np_star_two_rarefaction = 8.2983069275580775e+01\np_star_failsafe = 4.8079470435532315e+01\n   p^*_tilde  = 4.8079470435532315e+01\n   phi(p_*_t) = 4.5191687077384302e-01\n-> lambda_max = 1.0832159566199232e+01\n1.0832159566199232e+01\n\n\n1.0000000000000000e+00 -2.1800000000000002e+00 1.0000000000000000e-02\n1.0000000000000000e+00 -2.1800000000000002e+00 1.0000000000000000e+02\nrho_left: 1.0000000000000000e+00\nu_left: -2.1800000000000002e+00\np_left: 1.0000000000000000e-02\na_left: 1.1832159566199231e-01\nrho_right: 1.0000000000000000e+00\nu_right: -2.1800000000000002e+00\np_right: 1.0000000000000000e+02\na_right: 1.1832159566199232e+01\np_star_two_rarefaction = 8.2983069275580775e+01\np_star_failsafe = 4.8079470435532315e+01\n   p^*_tilde  = 4.8079470435532315e+01\n   phi(p_*_t) = 4.5191687077384302e-01\n-> lambda_max = 9.7758781271580943e+00\n9.7758781271580943e+00\n\n\n1.0000000000000000e-02 0.0000000000000000e+00 1.0000000000000000e-02\n1.0000000000000000e+03 0.0000000000000000e+00 1.0000000000000000e+03\nrho_left: 1.0000000000000000e-02\nu_left: 0.0000000000000000e+00\np_left: 1.0000000000000000e-02\na_left: 1.1832159566199232e+00\nrho_right: 1.0000000000000000e+03\nu_right: 0.0000000000000000e+00\np_right: 1.0000000000000000e+03\na_right: 1.1832159566199232e+00\np_star_two_rarefaction = 3.7200525124077216e-01\np_star_failsafe = 2.9291270455561289e+00\n   p^*_tilde  = 3.7200525124077216e-01\n   phi(p_*_t) = 1.4047089424401209e+00\n-> lambda_max = 6.6963146691962327e+00\n6.6963146691962327e+00\n\n\n1.0000000000000000e+00 2.1800000000000002e+00 1.0000000000000000e+02\n1.0000000000000000e+00 2.1800000000000002e+00 1.0000000000000000e-02\nrho_left: 1.0000000000000000e+00\nu_left: 2.1800000000000002e+00\np_left: 1.0000000000000000e+02\na_left: 1.1832159566199232e+01\nrho_right: 1.0000000000000000e+00\nu_right: 2.1800000000000002e+00\np_right: 1.0000000000000000e-02\na_right: 1.1832159566199231e-01\np_star_two_rarefaction = 8.2983069275580661e+01\np_star_failsafe = 4.8079470435532315e+01\n   p^*_tilde  = 4.8079470435532315e+01\n   phi(p_*_t) = 4.5191687077384302e-01\n-> lambda_max = 9.7758781271580943e+00\n9.7758781271580943e+00\n\n\n"
  },
  {
    "path": "tests/euler/verification-exact_riemann_solution-leblanc-1d-erk33.prm",
    "content": "subsection A - TimeLoop\n  set basename             = exactRP_sod-erk33\n\n  set enable compute error = true\n  set error quantities     = rho, m, E\n\n  set final time           = 0.1\n  set timer granularity    = 0.1\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = euler\n  set gamma     = 1.666666666667\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 0\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = 0\n    set position top right        = 1\n    set subdivisions x            = 100\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = exact riemann solution\n  set direction     = 1\n  set position      = 0.5\n\n  subsection exact riemann solution\n   set primitive state left =     1., 0., 0.666666666667e-1\n   set primitive state right = 1.e-3, 0., 0.666666666667e-10\n  end\nend\n\n\nsubsection F - HyperbolicModule\n  subsection limiter\n    set iterations            = 2\n    set newton max iterations = 2\n    set newton tolerance      = 1e-10\n    set relaxation factor     = 1\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.50\n  set cfl max               = 0.50\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler/verification-exact_riemann_solution-leblanc-1d-erk33.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 101\nt     = 0.1042186984501224\nLinf  = 0.4841628890737377\nL1    = 0.2390938310829922\nL2    = 0.2537994586498102\n"
  },
  {
    "path": "tests/euler/verification-exact_riemann_solution-sod-1d-erk33.prm",
    "content": "subsection A - TimeLoop\n  set basename             = exactRP_sod-erk33\n\n  set enable compute error = true\n  set error quantities     = rho, m, E\n\n  set final time           = 0.1\n  set timer granularity    = 0.1\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = euler\n  set gamma     = 1.4\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 0\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = 0\n    set position top right        = 1\n    set subdivisions x            = 100\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = exact riemann solution\n  set direction     = 1\n  set position      = 0.5\n\n  subsection exact riemann solution\n    set primitive state left  = 1.,    0, 1\n    set primitive state right = 0.125, 0, 0.1\n  end\nend\n\n\nsubsection F - HyperbolicModule\n  subsection limiter\n    set iterations            = 2\n    set newton max iterations = 2\n    set newton tolerance      = 1e-10\n    set relaxation factor     = 1\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.50\n  set cfl max               = 0.50\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler/verification-exact_riemann_solution-sod-1d-erk33.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 101\nt     = 0.1007647556591904\nLinf  = 0.4616559510609184\nL1    = 0.08998982692468323\nL2    = 0.1338223136148167\n"
  },
  {
    "path": "tests/euler/verification-isentropic_vortex-2d-erk33-l5-dg.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-euler-l5-dg\n\n  set enable compute error      = true\n\n  set final time                = 2.0\n\n  set timer granularity         = 2.0\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n  set gamma     = 1.4\nend\n\nsubsection C - Discretization\n  set finite element ansatz = dG Q1\n\n  set geometry        = rectangular domain\n  set mesh refinement     = 5\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection F - HyperbolicModule\n  subsection indicator\n    set evc factor = 0.0\n  end\n  subsection limiter\n    set relaxation factor     = 4.\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.2\n  set cfl max            = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler/verification-isentropic_vortex-2d-erk33-l5-dg.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 4096\nt     = 2.001343357700111\nLinf  = 0.05062300519154808\nL1    = 0.002799403617891262\nL2    = 0.006555357067732466\n"
  },
  {
    "path": "tests/euler/verification-isentropic_vortex-2d-erk33-l5-simplex.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-euler-l5-simplex\n\n  set enable compute error      = true\n\n  set final time                = 2.0\n\n  set timer granularity         = 2.0\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n  set gamma     = 1.4\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 5\n  set mesh type       = parallel shared\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n\n    set simplex mesh              = true\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.2\n  set cfl max            = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler/verification-isentropic_vortex-2d-erk33-l5-simplex.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 1089\nt     = 2.004026762575295\nLinf  = 0.05060492772107422\nL1    = 0.003391224544050118\nL2    = 0.007821923061063841\n"
  },
  {
    "path": "tests/euler/verification-isentropic_vortex-2d-erk33-l5.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-euler-l5\n\n  set enable compute error      = true\n\n  set final time                = 2.0\n\n  set timer granularity         = 2.0\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n  set gamma     = 1.4\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 5\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.2\n  set cfl max            = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler/verification-isentropic_vortex-2d-erk33-l5.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 1089\nt     = 2.005478356215103\nLinf  = 0.05684722982869379\nL1    = 0.003476202137585331\nL2    = 0.008732184894889809\n"
  },
  {
    "path": "tests/euler/verification-isentropic_vortex-2d-erk33-l6-dg.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-euler-l6-dg\n\n  set enable compute error      = true\n\n  set final time                = 2.0\n\n  set timer granularity         = 2.0\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n  set gamma     = 1.4\nend\n\nsubsection C - Discretization\n  set finite element ansatz = dG Q1\n\n  set geometry        = rectangular domain\n  set mesh refinement     = 6\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection F - HyperbolicModule\n  subsection indicator\n    set evc factor = 0.0\n  end\n  subsection limiter\n    set relaxation factor     = 4.\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.2\n  set cfl max            = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler/verification-isentropic_vortex-2d-erk33-l6-dg.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 16384\nt     = 2.001036743831844\nLinf  = 0.01180936293297689\nL1    = 0.0006260486517309677\nL2    = 0.001524324902497799\n"
  },
  {
    "path": "tests/euler/verification-isentropic_vortex-2d-erk33-l6-simplex.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-euler-l6-simplex\n\n  set enable compute error      = true\n\n  set final time                = 2.0\n\n  set timer granularity         = 2.0\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n  set gamma     = 1.4\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n  set mesh type       = parallel shared\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n\n    set simplex mesh              = true\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.2\n  set cfl max            = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler/verification-isentropic_vortex-2d-erk33-l6-simplex.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 4225\nt     = 2.004026763597627\nLinf  = 0.004725197322586651\nL1    = 0.0003895497125968039\nL2    = 0.0008890993770948166\n"
  },
  {
    "path": "tests/euler/verification-isentropic_vortex-2d-erk33-l6.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-euler-l6\n\n  set enable compute error      = true\n\n  set final time                = 2.0\n\n  set timer granularity         = 2.0\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n  set gamma     = 1.4\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.2\n  set cfl max            = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler/verification-isentropic_vortex-2d-erk33-l6.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 4225\nt     = 2.00547836223925\nLinf  = 0.005459595353028588\nL1    = 0.0004014078935420313\nL2    = 0.0009434929473865435\n"
  },
  {
    "path": "tests/euler/verification-isentropic_vortex-2d-erk33-l7-simplex.mpirun=4.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 16641\nt     = 2.00023125860023\nLinf  = 0.000698207775846866\nL1    = 5.046832799432538e-05\nL2    = 0.0001170446308613032\n"
  },
  {
    "path": "tests/euler/verification-isentropic_vortex-2d-erk33-l7-simplex.mpirun=8.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 16641\nt     = 2.00023125860023\nLinf  = 0.0006982077758240558\nL1    = 5.046832799478967e-05\nL2    = 0.000117044630861262\n"
  },
  {
    "path": "tests/euler/verification-isentropic_vortex-2d-erk33-l7-simplex.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-euler-l7-simplex\n\n  set enable compute error      = true\n\n  set final time                = 2.0\n\n  set timer granularity         = 2.0\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n  set gamma     = 1.4\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 7\n  set mesh type       = parallel shared\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n\n    set simplex mesh              = true\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.2\n  set cfl max            = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler/verification-isentropic_vortex-2d-erk33-l7.mpirun=4.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 16641\nt     = 2.005478363783096\nLinf  = 0.0006421457242927969\nL1    = 4.963809855061225e-05\nL2    = 0.0001163745979068702\n"
  },
  {
    "path": "tests/euler/verification-isentropic_vortex-2d-erk33-l7.mpirun=8.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 16641\nt     = 2.005478363783097\nLinf  = 0.0006421457242418914\nL1    = 4.963809855060513e-05\nL2    = 0.0001163745979068316\n"
  },
  {
    "path": "tests/euler/verification-isentropic_vortex-2d-erk33-l7.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-euler-l7\n\n  set enable compute error      = true\n\n  set final time                = 2.0\n\n  set timer granularity         = 2.0\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n  set gamma     = 1.4\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 7\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.2\n  set cfl max            = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler/verification-isentropic_vortex-2d-ssprk33-l5.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-euler-l5\n\n  set enable compute error      = true\n\n  set final time                = 2.0\n\n  set timer granularity         = 2.0\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n  set gamma     = 1.4\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 5\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.2\n  set cfl max            = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = ssprk 33\nend\n"
  },
  {
    "path": "tests/euler/verification-isentropic_vortex-2d-ssprk33-l5.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 1089\nt     = 2.005478356615953\nLinf  = 0.05677953271841615\nL1    = 0.003472892378428597\nL2    = 0.008728315528763941\n"
  },
  {
    "path": "tests/euler/verification-isentropic_vortex-2d-ssprk33-l6.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-euler-l6\n\n  set enable compute error      = true\n\n  set final time                = 2.0\n\n  set timer granularity         = 2.0\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n  set gamma     = 1.4\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.2\n  set cfl max            = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = ssprk 33\nend\n"
  },
  {
    "path": "tests/euler/verification-isentropic_vortex-2d-ssprk33-l6.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 4225\nt     = 2.00176451346406\nLinf  = 0.005445535976059313\nL1    = 0.0004004234492212183\nL2    = 0.0009414751441911138\n"
  },
  {
    "path": "tests/euler/verification-isentropic_vortex-2d-ssprk33-l7.mpirun=4.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 16641\nt     = 2.001764514969716\nLinf  = 0.0006406620822104935\nL1    = 4.953577956579434e-05\nL2    = 0.0001161423391282538\n"
  },
  {
    "path": "tests/euler/verification-isentropic_vortex-2d-ssprk33-l7.mpirun=8.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 16641\nt     = 2.001764514969716\nLinf  = 0.0006406620821988092\nL1    = 4.953577956579451e-05\nL2    = 0.0001161423391281092\n"
  },
  {
    "path": "tests/euler/verification-isentropic_vortex-2d-ssprk33-l7.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-euler-l7\n\n  set enable compute error      = true\n\n  set final time                = 2.0\n\n  set timer granularity         = 2.0\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler\n  set gamma     = 1.4\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 7\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.2\n  set cfl max            = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = ssprk 33\nend\n"
  },
  {
    "path": "tests/euler/verification-leblanc-1d-erk33-l6.mpirun=4.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 1601\nt     = 0.6666710022008712\nLinf  = 0.2233117555967211\nL1    = 0.01167724325117752\nL2    = 0.03259102542735855\n"
  },
  {
    "path": "tests/euler/verification-leblanc-1d-erk33-l6.prm",
    "content": "subsection A - TimeLoop\n  set basename             = leblanc-erk33\n\n  set enable compute error = true\n  set error quantities     = rho, m, E\n\n  set final time           = 0.66666666666667\n  set timer granularity    = 0.66666666666667\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = euler\n  set gamma     = 1.66666666666667\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = 0\n    set position top right        = 1\n    set subdivisions x            = 25\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = leblanc\n\n  set direction     = 1\n  # for cG put the discontinuity right in the center of a cell\n  set position      = 0.326732673267\nend\n\n\nsubsection F - HyperbolicModule\n  subsection limiter\n    set iterations            = 2\n    set newton max iterations = 2\n    set newton tolerance      = 1e-10\n    set relaxation factor     = 8\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.10\n  set cfl max               = 0.10\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler/verification-rarefaction-1d-erk33-l6.mpirun=4.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 1601\nt     = 0.3056208093406663\nLinf  = 0.001236294603917117\nL1    = 2.234263733795138e-05\nL2    = 8.123947460631401e-05\n"
  },
  {
    "path": "tests/euler/verification-rarefaction-1d-erk33-l6.prm",
    "content": "subsection A - TimeLoop\n  set basename             = leblanc-erk33\n\n  set enable compute error = true\n  set error quantities     = rho, m, E\n\n  set final time           = 0.30558\n  set timer granularity    = 0.30558\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = euler\n  set gamma     = 1.4\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = 0\n    set position top right        = 1\n    set subdivisions x            = 25\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = rarefaction\n\n  set direction     = 1\n  set position      = 0.2\nend\n\n\nsubsection F - HyperbolicModule\n  subsection limiter\n    set iterations            = 2\n    set newton max iterations = 2\n    set newton tolerance      = 1e-10\n    set relaxation factor     = 8\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.10\n  set cfl max               = 0.10\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler_aeos/CMakeLists.txt",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2022 - 2024 by the ryujin authors\n##\n\nset(EQUATION euler_aeos)\n\ninclude_directories(\n  ${CMAKE_BINARY_DIR}/source/\n  ${CMAKE_SOURCE_DIR}/source/${EQUATION}\n  ${CMAKE_SOURCE_DIR}/source/\n  )\n\nset(TEST_LIBRARIES obj_common obj_${EQUATION} obj_${EQUATION}_dependent)\nset(TEST_TARGET ryujin)\n\nif(TARGET obj_${EQUATION})\n  deal_ii_pickup_tests()\nendif()\n"
  },
  {
    "path": "tests/euler_aeos/dynamic_boundary_conditions-NASG.prm",
    "content": "##\n#\n# Test the Euler AEOS dynamic boundary conditions by setting up a\n# stationary isentropic vortex on a very small domain. We use the noble\n# Abel stiffened gas equation of state with nonzero covolume, reference\n# pressure p_\\infty and specific internal energy q.\n#\n# Note that the final error we are getting is the same as the one for\n# Dirichlet boundary conditions.\n#\n##\n\nsubsection A - TimeLoop\n  set basename             = test\n\n  set enable compute error = true\n  set error normalize      = true\n  set error quantities     = rho, m_1, m_2, E\n\n  set final time           = 2.0\n  set timer granularity    = 2.0\n\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler aeos\n\n  set equation of state = noble abel stiffened gas\n\n  subsection noble abel stiffened gas\n    set gamma                              = 1.8\n    set covolume b                         = 0.05\n    set reference pressure                 = 0.05\n    set reference specific internal energy = 0.10\n  end\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement     = 6\n\n  subsection rectangular domain\n    set boundary condition bottom = dynamic\n    set boundary condition left   = dynamic\n    set boundary condition right  = dynamic\n    set boundary condition top    = dynamic\n\n    set position bottom left      = -2, -2\n    set position top right        =  2,  2\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      =  0,  0\n\n  subsection isentropic vortex\n    set gamma       = 1.8\n    set mach number = 0\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.5\n  set cfl max               = 0.5\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler_aeos/dynamic_boundary_conditions-NASG.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler aeos« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 4225\nt     = 2.008283771328587\nLinf  = 0.03426798552371922\nL1    = 0.01514144692278083\nL2    = 0.01949964469314313\n"
  },
  {
    "path": "tests/euler_aeos/dynamic_boundary_conditions-PGE.prm",
    "content": "##\n#\n# Test the Euler AEOS dynamic boundary conditions by setting up a\n# stationary isentropic vortex on a very small domain. We use the\n# polytropic gas equation of state with gamma = 1.8.\n#\n##\n\nsubsection A - TimeLoop\n  set basename             = test\n\n  set enable compute error = true\n  set error normalize      = true\n  set error quantities     = rho, m_1, m_2, E\n\n  set final time           = 2.0\n  set timer granularity    = 2.0\n\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler aeos\n\n  set equation of state = polytropic gas\n\n  subsection polytropic gas\n    set gamma          = 1.80\n  end\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement     = 6\n\n  subsection rectangular domain\n    set boundary condition bottom = dynamic\n    set boundary condition left   = dynamic\n    set boundary condition right  = dynamic\n    set boundary condition top    = dynamic\n\n    set position bottom left      = -2, -2\n    set position top right        =  2,  2\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      =  0,  0\n\n  subsection isentropic vortex\n    set gamma       = 1.8\n    set mach number = 0\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.5\n  set cfl max               = 0.5\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler_aeos/dynamic_boundary_conditions-PGE.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler aeos« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 4225\nt     = 2.000939808443233\nLinf  = 0.004200050505829086\nL1    = 0.0005634110031633888\nL2    = 0.0006975698733519352\n"
  },
  {
    "path": "tests/euler_aeos/equation_of_state_library.cc",
    "content": "#include <equation_of_state_jones_wilkins_lee.h>\n#include <equation_of_state_noble_abel_stiffened_gas.h>\n#include <equation_of_state_polytropic_gas.h>\n#include <equation_of_state_sesame.h>\n#include <equation_of_state_simple_macaw.h>\n#include <equation_of_state_van_der_waals.h>\n\n#include <deal.II/base/array_view.h>\n#include <deal.II/lac/vector.h>\n\n#include <iomanip>\n#include <iostream>\n\n/*\n * Test the EOS library:\n */\n\nusing namespace ryujin::EquationOfStateLibrary;\nusing namespace ryujin;\nusing namespace dealii;\n\n/*\n * Create struct with default test values. Test values can be modified to\n * accomodate specific EOS.\n */\nstruct testValues {\n  double rho_scalar = 1.4;\n  double e_scalar = 1.0 / 1.4 / 0.4;\n\n  std::array<double, 5> rho_array{{1.4, 1.3, 1.2, 1.1, 1.0}};\n  std::array<double, 5> e_array{{0.3, 0.2, 0.1, 0.05, 0.025}};\n};\n\nvoid test(const ryujin::EquationOfStateLibrary::EquationOfState &eos,\n          const testValues &tv = testValues())\n{\n  const auto print_array =\n      [](const std::string name, const auto array, auto &ostream) {\n        ostream << name << \" =\";\n        for (const auto &it : array)\n          ostream << \" \" << it;\n        ostream << std::endl;\n      };\n\n  std::cout << std::setprecision(10);\n  std::cout << std::scientific;\n  std::cout << \"name = \" << eos.name() << std::endl;\n\n  {\n    auto rho = tv.rho_scalar;\n    auto e = tv.e_scalar;\n\n    const auto p = eos.pressure(rho, e);\n    const auto e_back = eos.specific_internal_energy(rho, p);\n    const auto c = eos.speed_of_sound(rho, e);\n    const auto T = eos.temperature(rho, e);\n\n    std::cout << \"input rho      = \" << rho << std::endl    //\n              << \"input e        = \" << e << std::endl      //\n              << \"output p       = \" << p << std::endl      //\n              << \"check e_back   = \" << e_back << std::endl //\n              << \"check c        = \" << c << std::endl      //\n              << \"check T        = \" << T << std::endl;\n  }\n\n  {\n    auto rho = tv.rho_array;\n    auto e = tv.e_array;\n\n    std::array<double, 5> p;\n    std::array<double, 5> e_back;\n    std::array<double, 5> c;\n    std::array<double, 5> T;\n\n    std::transform(\n        std::begin(rho),\n        std::end(rho),\n        std::begin(e),\n        std::begin(p),\n        [&](const auto rho, const auto e) { return eos.pressure(rho, e); });\n\n    std::transform(std::begin(rho),\n                   std::end(rho),\n                   std::begin(p),\n                   std::begin(e_back),\n                   [&](const auto rho, const auto p) {\n                     return eos.specific_internal_energy(rho, p);\n                   });\n\n    std::transform(std::begin(rho),\n                   std::end(rho),\n                   std::begin(e),\n                   std::begin(c),\n                   [&](const auto rho, const auto e) {\n                     return eos.speed_of_sound(rho, e);\n                   });\n\n    std::transform(\n        std::begin(rho),\n        std::end(rho),\n        std::begin(e),\n        std::begin(T),\n        [&](const auto rho, const auto e) { return eos.temperature(rho, e); });\n\n    print_array(\"input rho     \", rho, std::cout);\n    print_array(\"input e       \", e, std::cout);\n    print_array(\"output p      \", p, std::cout);\n    print_array(\"check e_back  \", e_back, std::cout);\n    print_array(\"check c       \", c, std::cout);\n    print_array(\"check T       \", T, std::cout);\n  }\n}\n\nint main()\n{\n  /* polytropic gas */\n\n  std::cout << \"\\nPolytropicGas with gamma=1.4\" << std::endl;\n  PolytropicGas polytropic_gas(\"\");\n  test(polytropic_gas);\n\n  /* Noble Abel stiffened gas */\n\n  std::cout << \"\\nNobleAbelStiffenedGas with gamma=1.4, b=0, q=0, pinf=0\"\n            << std::endl;\n  NobleAbelStiffenedGas noble_abel_stiffened_gas(\"\");\n  test(noble_abel_stiffened_gas);\n\n  std::cout\n      << \"\\nNobleAbelStiffenedGas with gamma=1.4, b=0.2, q=0.00125, pinf=0.005\"\n      << std::endl;\n  {\n    std::stringstream parameters;\n    parameters << \"subsection noble abel stiffened gas\\n\"\n               << \"set gamma = 1.4\\n\"\n               << \"set covolume b = 0.2\\n\"\n               << \"set reference specific internal energy = 0.00125\\n\"\n               << \"set reference pressure = 0.005\\n\"\n               << \"end\\n\"\n               << std::endl;\n    ParameterAcceptor::initialize(parameters);\n  }\n  test(noble_abel_stiffened_gas);\n\n  /* van der Waals */\n\n  std::cout << \"\\nVanDerWaals with gamma=1.4, a=0, b=0\" << std::endl;\n  VanDerWaals van_der_waals(\"\");\n  test(van_der_waals);\n\n  std::cout << \"\\nVanDerWaals with gamma=1.4, a=0.015, b=0.2\" << std::endl;\n  {\n    std::stringstream parameters;\n    parameters << \"subsection van der waals\\n\"\n               << \"set gamma = 1.40\\n\"\n               << \"set covolume b = 0.2\\n\"\n               << \"set vdw a = 0.015\\n\"\n               << \"end\\n\"\n               << std::endl;\n    ParameterAcceptor::initialize(parameters);\n  }\n  test(van_der_waals);\n\n  /* jones wilkins lee */\n\n  std::cout << \"\\nJonesWilkinsLee with omega=0.8938, A=6.3207e13, B=-4.472e9, \"\n               \"R1=11.3, R2=1.13, rho_0=1895, q_0=0\"\n            << std::endl;\n  JonesWilkinsLee jones_wilkins_lee(\"\");\n  test(jones_wilkins_lee);\n\n  std::cout << \"\\nJonesWilkinsLee with omega=0.4, A=0, B=0, \"\n               \"R1=1, R2=1, rho_0=1, q_0=0, c_v=1\"\n            << std::endl;\n  {\n    std::stringstream parameters;\n    parameters << \"subsection jones wilkins lee\\n\"\n               << \"set A     = 0\\n\"\n               << \"set B     = 0\\n\"\n               << \"set R1    = 1\\n\"\n               << \"set R2    = 1\\n\"\n               << \"set omega = 0.4\\n\"\n               << \"set rho_0 = 1\\n\"\n               << \"set q_0   = 0\\n\"\n               << \"set c_v   = 1\\n\"\n               << \"end\\n\"\n               << std::endl;\n    ParameterAcceptor::initialize(parameters);\n  }\n  test(jones_wilkins_lee);\n\n  /* simple macaw */\n  testValues macaw_values;\n  macaw_values.rho_scalar = 1.86;\n  macaw_values.e_scalar = 1.49017421e2;\n  macaw_values.rho_array = {2., 2.5, 3., 3.5, 4.0};\n  macaw_values.e_array = {1.5e2, 1.4e2, 1.3e2, 1.2e2, 1.1e2};\n\n  std::cout << \"\\nSimple Macaw with default parameters\" << std::endl;\n  SimpleMacaw simple_macaw(\"\");\n  {\n    std::stringstream parameters;\n    parameters << \"subsection simple macaw\\n\"\n               << \"set A                           = 7.3\\n\"\n               << \"set B                           = 3.9\\n\"\n               << \"set Gamma                       = 0.5\\n\"\n               << \"set cvInf                       = 0.000389\\n\"\n               << \"set reference T0                = 150\\n\"\n               << \"set reference rho0              = 8.952\\n\"\n               << \"end\\n\"\n               << std::endl;\n\n    ParameterAcceptor::initialize(parameters);\n  }\n  test(simple_macaw, macaw_values);\n\n  return 0;\n}\n"
  },
  {
    "path": "tests/euler_aeos/equation_of_state_library.threads=2.output",
    "content": "\nPolytropicGas with gamma=1.4\nname = polytropic gas\ninput rho      = 1.4000000000e+00\ninput e        = 1.7857142857e+00\noutput p       = 1.0000000000e+00\ncheck e_back   = 1.7857142857e+00\ncheck c        = 1.0000000000e+00\ncheck T        = 2.4883419711e-03\ninput rho      = 1.4000000000e+00 1.3000000000e+00 1.2000000000e+00 1.1000000000e+00 1.0000000000e+00\ninput e        = 3.0000000000e-01 2.0000000000e-01 1.0000000000e-01 5.0000000000e-02 2.5000000000e-02\noutput p       = 1.6800000000e-01 1.0400000000e-01 4.8000000000e-02 2.2000000000e-02 1.0000000000e-02\ncheck e_back   = 3.0000000000e-01 2.0000000000e-01 1.0000000000e-01 5.0000000000e-02 2.5000000000e-02\ncheck c        = 4.0987803064e-01 3.3466401061e-01 2.3664319132e-01 1.6733200531e-01 1.1832159566e-01\ncheck T        = 4.1804145114e-04 2.7869430076e-04 1.3934715038e-04 6.9673575189e-05 3.4836787595e-05\n\nNobleAbelStiffenedGas with gamma=1.4, b=0, q=0, pinf=0\nname = noble abel stiffened gas\ninput rho      = 1.4000000000e+00\ninput e        = 1.7857142857e+00\noutput p       = 1.0000000000e+00\ncheck e_back   = 1.7857142857e+00\ncheck c        = 1.0000000000e+00\ncheck T        = 2.4883419711e-03\ninput rho      = 1.4000000000e+00 1.3000000000e+00 1.2000000000e+00 1.1000000000e+00 1.0000000000e+00\ninput e        = 3.0000000000e-01 2.0000000000e-01 1.0000000000e-01 5.0000000000e-02 2.5000000000e-02\noutput p       = 1.6800000000e-01 1.0400000000e-01 4.8000000000e-02 2.2000000000e-02 1.0000000000e-02\ncheck e_back   = 3.0000000000e-01 2.0000000000e-01 1.0000000000e-01 5.0000000000e-02 2.5000000000e-02\ncheck c        = 4.0987803064e-01 3.3466401061e-01 2.3664319132e-01 1.6733200531e-01 1.1832159566e-01\ncheck T        = 4.1804145114e-04 2.7869430076e-04 1.3934715038e-04 6.9673575189e-05 3.4836787595e-05\n\nNobleAbelStiffenedGas with gamma=1.4, b=0.2, q=0.00125, pinf=0.005\nname = noble abel stiffened gas\ninput rho      = 1.4000000000e+00\ninput e        = 1.7857142857e+00\noutput p       = 1.3809166667e+00\ncheck e_back   = 1.7857142857e+00\ncheck c        = 1.3874019819e+00\ncheck T        = 2.4830169192e-03\ninput rho      = 1.4000000000e+00 1.3000000000e+00 1.2000000000e+00 1.1000000000e+00 1.0000000000e+00\ninput e        = 3.0000000000e-01 2.0000000000e-01 1.0000000000e-01 5.0000000000e-02 2.5000000000e-02\noutput p       = 2.2536111111e-01 1.3266216216e-01 5.5368421053e-02 2.0500000000e-02 4.8750000000e-03\ncheck e_back   = 3.0000000000e-01 2.0000000000e-01 1.0000000000e-01 5.0000000000e-02 2.5000000000e-02\ncheck c        = 5.6563768231e-01 4.4759350412e-01 3.0441882628e-01 2.0398135113e-01 1.3145816825e-01\ncheck T        = 4.1271639932e-04 2.7298642710e-04 1.3319265124e-04 6.2991245933e-05 2.7521062200e-05\n\nVanDerWaals with gamma=1.4, a=0, b=0\nname = van der waals\ninput rho      = 1.4000000000e+00\ninput e        = 1.7857142857e+00\noutput p       = 1.0000000000e+00\ncheck e_back   = 1.7857142857e+00\ncheck c        = 1.0000000000e+00\ncheck T        = 1.7857142857e+00\ninput rho      = 1.4000000000e+00 1.3000000000e+00 1.2000000000e+00 1.1000000000e+00 1.0000000000e+00\ninput e        = 3.0000000000e-01 2.0000000000e-01 1.0000000000e-01 5.0000000000e-02 2.5000000000e-02\noutput p       = 1.6800000000e-01 1.0400000000e-01 4.8000000000e-02 2.2000000000e-02 1.0000000000e-02\ncheck e_back   = 3.0000000000e-01 2.0000000000e-01 1.0000000000e-01 5.0000000000e-02 2.5000000000e-02\ncheck c        = 4.0987803064e-01 3.3466401061e-01 2.3664319132e-01 1.6733200531e-01 1.1832159566e-01\ncheck T        = 3.0000000000e-01 2.0000000000e-01 1.0000000000e-01 5.0000000000e-02 2.5000000000e-02\n\nVanDerWaals with gamma=1.4, a=0.015, b=0.2\nname = van der waals\ninput rho      = 1.4000000000e+00\ninput e        = 1.7857142857e+00\noutput p       = 1.3758222222e+00\ncheck e_back   = 1.7857142857e+00\ncheck c        = 1.3819180623e+00\ncheck T        = 1.8067142857e+00\ninput rho      = 1.4000000000e+00 1.3000000000e+00 1.2000000000e+00 1.1000000000e+00 1.0000000000e+00\ninput e        = 3.0000000000e-01 2.0000000000e-01 1.0000000000e-01 5.0000000000e-02 2.5000000000e-02\noutput p       = 2.2026666667e-01 1.2889324324e-01 5.2926315789e-02 1.9362820513e-02 5.0000000000e-03\ncheck e_back   = 3.0000000000e-01 2.0000000000e-01 1.0000000000e-01 5.0000000000e-02 2.5000000000e-02\ncheck c        = 5.5205005141e-01 4.3066276408e-01 2.8000791441e-01 1.6795752570e-01 7.0710678119e-02\ncheck T        = 3.2100000000e-01 2.1950000000e-01 1.1800000000e-01 6.6500000000e-02 4.0000000000e-02\n\nJonesWilkinsLee with omega=0.8938, A=6.3207e13, B=-4.472e9, R1=11.3, R2=1.13, rho_0=1895, q_0=0\nname = jones wilkins lee\ninput rho      = 1.4000000000e+00\ninput e        = 1.7857142857e+00\noutput p       = 2.2345000000e+00\ncheck e_back   = 1.7857142857e+00\ncheck c        = 1.7385741490e+00\ncheck T        = 1.3606467919e+00\ninput rho      = 1.4000000000e+00 1.3000000000e+00 1.2000000000e+00 1.1000000000e+00 1.0000000000e+00\ninput e        = 3.0000000000e-01 2.0000000000e-01 1.0000000000e-01 5.0000000000e-02 2.5000000000e-02\noutput p       = 3.7539600000e-01 2.3238800000e-01 1.0725600000e-01 4.9159000000e-02 2.2345000000e-02\ncheck e_back   = 3.0000000000e-01 2.0000000000e-01 1.0000000000e-01 5.0000000000e-02 2.5000000000e-02\ncheck c        = 7.1260334829e-01 5.8183819744e-01 4.1142173496e-01 2.9091909872e-01 2.0571086748e-01\ncheck T        = 2.2858866104e-01 1.5239244069e-01 7.6196220346e-02 3.8098110173e-02 1.9049055086e-02\n\nJonesWilkinsLee with omega=0.4, A=0, B=0, R1=1, R2=1, rho_0=1, q_0=0, c_v=1\nname = jones wilkins lee\ninput rho      = 1.4000000000e+00\ninput e        = 1.7857142857e+00\noutput p       = 1.0000000000e+00\ncheck e_back   = 1.7857142857e+00\ncheck c        = 1.0000000000e+00\ncheck T        = 1.7857142857e+00\ninput rho      = 1.4000000000e+00 1.3000000000e+00 1.2000000000e+00 1.1000000000e+00 1.0000000000e+00\ninput e        = 3.0000000000e-01 2.0000000000e-01 1.0000000000e-01 5.0000000000e-02 2.5000000000e-02\noutput p       = 1.6800000000e-01 1.0400000000e-01 4.8000000000e-02 2.2000000000e-02 1.0000000000e-02\ncheck e_back   = 3.0000000000e-01 2.0000000000e-01 1.0000000000e-01 5.0000000000e-02 2.5000000000e-02\ncheck c        = 4.0987803064e-01 3.3466401061e-01 2.3664319132e-01 1.6733200531e-01 1.1832159566e-01\ncheck T        = 3.0000000000e-01 2.0000000000e-01 1.0000000000e-01 5.0000000000e-02 2.5000000000e-02\n\nSimple Macaw with default parameters\nname = simple macaw\ninput rho      = 1.8600000000e+00\ninput e        = 1.4901742100e+02\noutput p       = 9.9608500036e+01\ncheck e_back   = 1.4901742100e+02\ncheck c        = 1.0164285205e+01\ncheck T        = 3.5406565124e+05\ninput rho      = 2.0000000000e+00 2.5000000000e+00 3.0000000000e+00 3.5000000000e+00 4.0000000000e+00\ninput e        = 1.5000000000e+02 1.4000000000e+02 1.3000000000e+02 1.2000000000e+02 1.1000000000e+02\noutput p       = 1.1130680386e+02 1.3733758995e+02 1.5840566011e+02 1.7453664460e+02 1.8576567780e+02\ncheck e_back   = 1.5000000000e+02 1.4000000000e+02 1.3000000000e+02 1.2000000000e+02 1.1000000000e+02\ncheck c        = 1.0240307510e+01 9.9779392544e+00 9.6741906989e+00 9.3423972452e+00 8.9891917050e+00\ncheck T        = 3.5934696579e+05 3.4095873685e+05 3.2012352516e+05 2.9788435082e+05 2.7476095669e+05\n"
  },
  {
    "path": "tests/euler_aeos/hyperbolic_system.cc",
    "content": "#include <hyperbolic_system.h>\n#include <simd.h>\n\n#include <deal.II/base/vectorization.h>\n\n#include <iomanip>\n#include <iostream>\n\n/*\n * Test EOS independent functions:\n */\n\nusing namespace ryujin::EulerAEOS;\nusing namespace ryujin;\nusing namespace dealii;\n\n\nstatic HyperbolicSystem hyperbolic_system;\n\n\ntemplate <int dim, typename Number>\nvoid test(const Number gamma)\n{\n  std::cout << std::setprecision(10);\n  std::cout << std::scientific;\n\n  std::cout << \"interpolatory covolume: \"\n            << hyperbolic_system.view<dim, Number>().eos_interpolation_b()\n            << std::endl;\n\n  const auto view = hyperbolic_system.view<dim, Number>();\n\n  using View = HyperbolicSystemView<dim, Number>;\n  using state_type = typename View::state_type;\n\n  const auto from_1d_state =\n      [](const dealii::Tensor<1, 3, Number> &state_1d) -> state_type {\n    const auto &rho = state_1d[0];\n    const auto &u = state_1d[1];\n    const auto &e = state_1d[2]; /* specific internal energy */\n\n    state_type state;\n\n    state[0] = rho;\n    state[1] = rho * u;\n    state[dim + 1] = rho * e + 0.5 * rho * u * u;\n\n    return state;\n  };\n\n  dealii::Tensor<1, 3, Number> state_1d;\n  state_1d[0] = gamma;\n  state_1d[1] = 3.;\n  state_1d[2] = 1. / gamma / (gamma - 1.0); /* specific internal energy */\n  const auto U = from_1d_state(state_1d);\n\n  std::cout << \"dim = \" << dim << std::endl;\n  std::cout << \"density = \"                                            //\n            << view.density(U)                                         //\n            << std::endl;                                              //\n  std::cout << \"momentum = \"                                           //\n            << view.momentum(U)                                        //\n            << std::endl;                                              //\n  std::cout << \"total_energy = \"                                       //\n            << view.total_energy(U)                                    //\n            << std::endl;                                              //\n  std::cout << \"internal_energy = \"                                    //\n            << view.internal_energy(U)                                 //\n            << std::endl;                                              //\n  std::cout << \"internal_energy_derivative = \"                         //\n            << view.internal_energy_derivative(U)                      //\n            << std::endl;                                              //\n  std::cout << \"specific_entropy = \"                                   //\n            << view.surrogate_specific_entropy(U, gamma)               //\n            << std::endl;                                              //\n  std::cout << \"harten entropy = \"                                     //\n            << view.surrogate_harten_entropy(U, gamma)                 //\n            << std::endl;                                              //\n  const auto eta = view.surrogate_harten_entropy(U, gamma);            //\n  std::cout << \"harten_entropy_derivative = \"                          //\n            << view.surrogate_harten_entropy_derivative(U, eta, gamma) //\n            << std::endl;                                              //\n  const auto p = view.surrogate_pressure(U, gamma);                    //\n  std::cout << \"surrogate_pressure = \"                                 //\n            << view.surrogate_pressure(U, gamma)                       //\n            << std::endl;                                              //\n  std::cout << \"surrogate_gamma = \"                                    //\n            << view.surrogate_gamma(U, p)                              //\n            << std::endl;                                              //\n  std::cout << \"f = \"                                                  //\n            << view.f(U, p)                                            //\n            << std::endl;                                              //\n}\n\nint main()\n{\n  const auto set_covolume = [&](const double covolume) {\n    /*\n     * Set the interpolatory covolume by selecting an equation of state\n     * with a covolume.\n     */\n    std::stringstream parameters;\n    parameters << \"subsection HyperbolicSystem\\n\"\n               << \"set equation of state = van der waals\\n\"\n               << \"subsection van der waals\\n\"\n               << \"set covolume b = \" << std::to_string(covolume) << \"\\n\"\n               << \"end\\n\"\n               << \"end\\n\"\n               << std::endl;\n    ParameterAcceptor::initialize(parameters);\n  };\n\n  std::cout << \"\\ndouble:\\n\" << std::endl;\n  test<1, double>(/* surrogate gamma */ 1.4);\n  test<2, double>(/* surrogate gamma */ 1.4);\n  test<3, double>(/* surrogate gamma */ 1.4);\n  std::cout << \"\\nfloat:\\n\" << std::endl;\n  test<1, float>(/* surrogate gamma */ 1.4);\n  test<2, float>(/* surrogate gamma */ 1.4);\n  test<3, float>(/* surrogate gamma */ 1.4);\n\n  std::cout << \"\\ndouble:\\n\" << std::endl;\n  test<1, double>(/* surrogate gamma */ 1.9);\n  test<2, double>(/* surrogate gamma */ 1.9);\n  test<3, double>(/* surrogate gamma */ 1.9);\n  std::cout << \"\\nfloat:\\n\" << std::endl;\n  test<1, float>(/* surrogate gamma */ 1.9);\n  test<2, float>(/* surrogate gamma */ 1.9);\n  test<3, float>(/* surrogate gamma */ 1.9);\n\n\n  set_covolume(0.1);\n  std::cout << \"\\ndouble:\\n\" << std::endl;\n  test<1, double>(/* surrogate gamma */ 1.4);\n  test<2, double>(/* surrogate gamma */ 1.4);\n  test<3, double>(/* surrogate gamma */ 1.4);\n  std::cout << \"\\nfloat:\\n\" << std::endl;\n  test<1, float>(/* surrogate gamma */ 1.4);\n  test<2, float>(/* surrogate gamma */ 1.4);\n  test<3, float>(/* surrogate gamma */ 1.4);\n\n  std::cout << \"\\ndouble:\\n\" << std::endl;\n  test<1, double>(/* surrogate gamma */ 1.9);\n  test<2, double>(/* surrogate gamma */ 1.9);\n  test<3, double>(/* surrogate gamma */ 1.9);\n  std::cout << \"\\nfloat:\\n\" << std::endl;\n  test<1, float>(/* surrogate gamma */ 1.9);\n  test<2, float>(/* surrogate gamma */ 1.9);\n  test<3, float>(/* surrogate gamma */ 1.9);\n\n  set_covolume(0.5);\n  std::cout << \"\\ndouble:\\n\" << std::endl;\n  test<1, double>(/* surrogate gamma */ 1.4);\n  test<2, double>(/* surrogate gamma */ 1.4);\n  test<3, double>(/* surrogate gamma */ 1.4);\n  std::cout << \"\\nfloat:\\n\" << std::endl;\n  test<1, float>(/* surrogate gamma */ 1.4);\n  test<2, float>(/* surrogate gamma */ 1.4);\n  test<3, float>(/* surrogate gamma */ 1.4);\n\n  std::cout << \"\\ndouble:\\n\" << std::endl;\n  test<1, double>(/* surrogate gamma */ 1.9);\n  test<2, double>(/* surrogate gamma */ 1.9);\n  test<3, double>(/* surrogate gamma */ 1.9);\n  std::cout << \"\\nfloat:\\n\" << std::endl;\n  test<1, float>(/* surrogate gamma */ 1.9);\n  test<2, float>(/* surrogate gamma */ 1.9);\n  test<3, float>(/* surrogate gamma */ 1.9);\n\n  return 0;\n}\n"
  },
  {
    "path": "tests/euler_aeos/hyperbolic_system.threads=2.output",
    "content": "\ndouble:\n\ninterpolatory covolume: 0.0000000000e+00\ndim = 1\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 2.8089484437e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 1.3600000000e+01 2.9400000000e+01\ninterpolatory covolume: 0.0000000000e+00\ndim = 2\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 0.0000000000e+00 2.8089484437e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 1.3600000000e+01 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 2.9400000000e+01 0.0000000000e+00\ninterpolatory covolume: 0.0000000000e+00\ndim = 3\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 0.0000000000e+00 0.0000000000e+00 2.8089484437e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3600000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 2.9400000000e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 0.0000000000e+00\ndim = 1\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656244040e+00 -8.4268432856e-01 2.8089478612e-01\nsurrogate_pressure = 1.0000003576e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 1.3599999428e+01 2.9400001526e+01\ninterpolatory covolume: 0.0000000000e+00\ndim = 2\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656244040e+00 -8.4268432856e-01 0.0000000000e+00 2.8089478612e-01\nsurrogate_pressure = 1.0000003576e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 1.3599999428e+01 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 2.9400001526e+01 0.0000000000e+00\ninterpolatory covolume: 0.0000000000e+00\ndim = 3\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656244040e+00 -8.4268432856e-01 0.0000000000e+00 0.0000000000e+00 2.8089478612e-01\nsurrogate_pressure = 1.0000003576e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 1.3599999428e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 2.9400001526e+01 0.0000000000e+00 0.0000000000e+00\n\ndouble:\n\ninterpolatory covolume: 0.0000000000e+00\ndim = 1\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819023037e-01\nharten entropy = 1.2938989975e+00\nharten_entropy_derivative = 2.0418242801e+00 -1.2046645839e+00 4.0155486130e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 1.8100000000e+01 3.1983333333e+01\ninterpolatory covolume: 0.0000000000e+00\ndim = 2\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819023037e-01\nharten entropy = 1.2938989975e+00\nharten_entropy_derivative = 2.0418242801e+00 -1.2046645839e+00 0.0000000000e+00 4.0155486130e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 1.8100000000e+01 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 3.1983333333e+01 0.0000000000e+00\ninterpolatory covolume: 0.0000000000e+00\ndim = 3\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819023037e-01\nharten entropy = 1.2938989975e+00\nharten_entropy_derivative = 2.0418242801e+00 -1.2046645839e+00 0.0000000000e+00 0.0000000000e+00 4.0155486130e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00 1.8100000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 3.1983333333e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 0.0000000000e+00\ndim = 1\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819026709e-01\nharten entropy = 1.2938990593e+00\nharten_entropy_derivative = 2.0418241024e+00 -1.2046644688e+00 4.0155482292e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 1.8099998474e+01 3.1983331680e+01\ninterpolatory covolume: 0.0000000000e+00\ndim = 2\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819026709e-01\nharten entropy = 1.2938990593e+00\nharten_entropy_derivative = 2.0418241024e+00 -1.2046644688e+00 0.0000000000e+00 4.0155482292e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 1.8099998474e+01 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 3.1983331680e+01 0.0000000000e+00\ninterpolatory covolume: 0.0000000000e+00\ndim = 3\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819026709e-01\nharten entropy = 1.2938990593e+00\nharten_entropy_derivative = 2.0418241024e+00 -1.2046644688e+00 0.0000000000e+00 0.0000000000e+00 4.0155482292e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00 1.8099998474e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 3.1983331680e+01 0.0000000000e+00 0.0000000000e+00\n\ndouble:\n\ninterpolatory covolume: 1.0000000000e-01\ndim = 1\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694680208e+00\nharten entropy = 1.6435317327e+00\nharten_entropy_derivative = 1.6899437583e+00 -8.2176586633e-01 2.7392195544e-01\nsurrogate_pressure = 1.1627906977e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 1.3762790698e+01 2.9888372093e+01\ninterpolatory covolume: 1.0000000000e-01\ndim = 2\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694680208e+00\nharten entropy = 1.6435317327e+00\nharten_entropy_derivative = 1.6899437583e+00 -8.2176586633e-01 0.0000000000e+00 2.7392195544e-01\nsurrogate_pressure = 1.1627906977e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 1.3762790698e+01 0.0000000000e+00 0.0000000000e+00 1.1627906977e+00 2.9888372093e+01 0.0000000000e+00\ninterpolatory covolume: 1.0000000000e-01\ndim = 3\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694680208e+00\nharten entropy = 1.6435317327e+00\nharten_entropy_derivative = 1.6899437583e+00 -8.2176586633e-01 0.0000000000e+00 0.0000000000e+00 2.7392195544e-01\nsurrogate_pressure = 1.1627906977e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3762790698e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.1627906977e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.1627906977e+00 2.9888372093e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 1.0000000149e-01\ndim = 1\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694685936e+00\nharten entropy = 1.6435320377e+00\nharten_entropy_derivative = 1.6899434328e+00 -8.2176560163e-01 2.7392187715e-01\nsurrogate_pressure = 1.1627911329e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 1.3762790680e+01 2.9888372421e+01\ninterpolatory covolume: 1.0000000149e-01\ndim = 2\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694685936e+00\nharten entropy = 1.6435320377e+00\nharten_entropy_derivative = 1.6899434328e+00 -8.2176560163e-01 0.0000000000e+00 2.7392187715e-01\nsurrogate_pressure = 1.1627911329e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 1.3762790680e+01 0.0000000000e+00 0.0000000000e+00 1.1627911329e+00 2.9888372421e+01 0.0000000000e+00\ninterpolatory covolume: 1.0000000149e-01\ndim = 3\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694685936e+00\nharten entropy = 1.6435320377e+00\nharten_entropy_derivative = 1.6899434328e+00 -8.2176560163e-01 0.0000000000e+00 0.0000000000e+00 2.7392187715e-01\nsurrogate_pressure = 1.1627911329e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 1.3762790680e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.1627911329e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.1627911329e+00 2.9888372421e+01 0.0000000000e+00 0.0000000000e+00\n\ndouble:\n\ninterpolatory covolume: 1.0000000000e-01\ndim = 1\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149520626e-01\nharten entropy = 1.2119903874e+00\nharten_entropy_derivative = 1.8661328096e+00 -1.1284048434e+00 3.7613494781e-01\nsurrogate_pressure = 1.2345679012e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 1.8334567901e+01 3.2687037037e+01\ninterpolatory covolume: 1.0000000000e-01\ndim = 2\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149520626e-01\nharten entropy = 1.2119903874e+00\nharten_entropy_derivative = 1.8661328096e+00 -1.1284048434e+00 0.0000000000e+00 3.7613494781e-01\nsurrogate_pressure = 1.2345679012e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 1.8334567901e+01 0.0000000000e+00 0.0000000000e+00 1.2345679012e+00 3.2687037037e+01 0.0000000000e+00\ninterpolatory covolume: 1.0000000000e-01\ndim = 3\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149520626e-01\nharten entropy = 1.2119903874e+00\nharten_entropy_derivative = 1.8661328096e+00 -1.1284048434e+00 0.0000000000e+00 0.0000000000e+00 3.7613494781e-01\nsurrogate_pressure = 1.2345679012e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00 1.8334567901e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.2345679012e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.2345679012e+00 3.2687037037e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 1.0000000149e-01\ndim = 1\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149522305e-01\nharten entropy = 1.2119904757e+00\nharten_entropy_derivative = 1.8661326170e+00 -1.1284047365e+00 3.7613490224e-01\nsurrogate_pressure = 1.2345678806e+00\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 1.8334566116e+01 3.2687034607e+01\ninterpolatory covolume: 1.0000000149e-01\ndim = 2\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149522305e-01\nharten entropy = 1.2119904757e+00\nharten_entropy_derivative = 1.8661326170e+00 -1.1284047365e+00 0.0000000000e+00 3.7613490224e-01\nsurrogate_pressure = 1.2345678806e+00\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 1.8334566116e+01 0.0000000000e+00 0.0000000000e+00 1.2345678806e+00 3.2687034607e+01 0.0000000000e+00\ninterpolatory covolume: 1.0000000149e-01\ndim = 3\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149522305e-01\nharten entropy = 1.2119904757e+00\nharten_entropy_derivative = 1.8661326170e+00 -1.1284047365e+00 0.0000000000e+00 0.0000000000e+00 3.7613490224e-01\nsurrogate_pressure = 1.2345678806e+00\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00 1.8334566116e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.2345678806e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.2345678806e+00 3.2687034607e+01 0.0000000000e+00 0.0000000000e+00\n\ndouble:\n\ninterpolatory covolume: 5.0000000000e-01\ndim = 1\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429354725e-01\nharten entropy = 1.3789501326e+00\nharten_entropy_derivative = 1.0615727211e+00 -6.8947506630e-01 2.2982502210e-01\nsurrogate_pressure = 3.3333333333e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 1.5933333333e+01 3.6400000000e+01\ninterpolatory covolume: 5.0000000000e-01\ndim = 2\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429354725e-01\nharten entropy = 1.3789501326e+00\nharten_entropy_derivative = 1.0615727211e+00 -6.8947506630e-01 0.0000000000e+00 2.2982502210e-01\nsurrogate_pressure = 3.3333333333e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 1.5933333333e+01 0.0000000000e+00 0.0000000000e+00 3.3333333333e+00 3.6400000000e+01 0.0000000000e+00\ninterpolatory covolume: 5.0000000000e-01\ndim = 3\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429354725e-01\nharten entropy = 1.3789501326e+00\nharten_entropy_derivative = 1.0615727211e+00 -6.8947506630e-01 0.0000000000e+00 0.0000000000e+00 2.2982502210e-01\nsurrogate_pressure = 3.3333333333e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 1.5933333333e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 3.3333333333e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 3.3333333333e+00 3.6400000000e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 5.0000000000e-01\ndim = 1\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429401636e-01\nharten entropy = 1.3789503574e+00\nharten_entropy_derivative = 1.0615724325e+00 -6.8947488070e-01 2.2982497513e-01\nsurrogate_pressure = 3.3333344460e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 1.5933334351e+01 3.6400005341e+01\ninterpolatory covolume: 5.0000000000e-01\ndim = 2\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429401636e-01\nharten entropy = 1.3789503574e+00\nharten_entropy_derivative = 1.0615724325e+00 -6.8947488070e-01 0.0000000000e+00 2.2982497513e-01\nsurrogate_pressure = 3.3333344460e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 1.5933334351e+01 0.0000000000e+00 0.0000000000e+00 3.3333344460e+00 3.6400005341e+01 0.0000000000e+00\ninterpolatory covolume: 5.0000000000e-01\ndim = 3\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429401636e-01\nharten entropy = 1.3789503574e+00\nharten_entropy_derivative = 1.0615724325e+00 -6.8947488070e-01 0.0000000000e+00 0.0000000000e+00 2.2982497513e-01\nsurrogate_pressure = 3.3333344460e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 1.5933334351e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 3.3333344460e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 3.3333344460e+00 3.6400005341e+01 0.0000000000e+00 0.0000000000e+00\n\ndouble:\n\ninterpolatory covolume: 5.0000000000e-01\ndim = 1\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141072431e-02\nharten entropy = 5.1066071232e-01\nharten_entropy_derivative = -7.7896611380e-01 -4.7544273216e-01 1.5848091072e-01\nsurrogate_pressure = 2.0000000000e+01\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 3.7100000000e+01 8.8983333333e+01\ninterpolatory covolume: 5.0000000000e-01\ndim = 2\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141072431e-02\nharten entropy = 5.1066071232e-01\nharten_entropy_derivative = -7.7896611380e-01 -4.7544273216e-01 0.0000000000e+00 1.5848091072e-01\nsurrogate_pressure = 2.0000000000e+01\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 3.7100000000e+01 0.0000000000e+00 0.0000000000e+00 2.0000000000e+01 8.8983333333e+01 0.0000000000e+00\ninterpolatory covolume: 5.0000000000e-01\ndim = 3\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141072431e-02\nharten entropy = 5.1066071232e-01\nharten_entropy_derivative = -7.7896611380e-01 -4.7544273216e-01 0.0000000000e+00 0.0000000000e+00 1.5848091072e-01\nsurrogate_pressure = 2.0000000000e+01\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00 3.7100000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 2.0000000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 2.0000000000e+01 8.8983333333e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 5.0000000000e-01\ndim = 1\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141100839e-02\nharten entropy = 5.1066082716e-01\nharten_entropy_derivative = -7.7896571159e-01 -4.7544255853e-01 1.5848085284e-01\nsurrogate_pressure = 1.9999996185e+01\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 3.7099994659e+01 8.8983314514e+01\ninterpolatory covolume: 5.0000000000e-01\ndim = 2\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141100839e-02\nharten entropy = 5.1066082716e-01\nharten_entropy_derivative = -7.7896571159e-01 -4.7544255853e-01 0.0000000000e+00 1.5848085284e-01\nsurrogate_pressure = 1.9999996185e+01\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 3.7099994659e+01 0.0000000000e+00 0.0000000000e+00 1.9999996185e+01 8.8983314514e+01 0.0000000000e+00\ninterpolatory covolume: 5.0000000000e-01\ndim = 3\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141100839e-02\nharten entropy = 5.1066082716e-01\nharten_entropy_derivative = -7.7896571159e-01 -4.7544255853e-01 0.0000000000e+00 0.0000000000e+00 1.5848085284e-01\nsurrogate_pressure = 1.9999996185e+01\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00 3.7099994659e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.9999996185e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.9999996185e+01 8.8983314514e+01 0.0000000000e+00 0.0000000000e+00\n"
  },
  {
    "path": "tests/euler_aeos/hyperbolic_system.threads=2.output.gcc-13.3-avx2",
    "content": "\ndouble:\n\ninterpolatory covolume: 0.0000000000e+00\ndim = 1\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 2.8089484437e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 1.3600000000e+01 2.9400000000e+01\ninterpolatory covolume: 0.0000000000e+00\ndim = 2\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 0.0000000000e+00 2.8089484437e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 1.3600000000e+01 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 2.9400000000e+01 0.0000000000e+00\ninterpolatory covolume: 0.0000000000e+00\ndim = 3\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 0.0000000000e+00 0.0000000000e+00 2.8089484437e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3600000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 2.9400000000e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 0.0000000000e+00\ndim = 1\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656244040e+00 -8.4268432856e-01 2.8089478612e-01\nsurrogate_pressure = 1.0000003576e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 1.3599999428e+01 2.9400001526e+01\ninterpolatory covolume: 0.0000000000e+00\ndim = 2\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656244040e+00 -8.4268432856e-01 0.0000000000e+00 2.8089478612e-01\nsurrogate_pressure = 1.0000003576e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 1.3599999428e+01 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 2.9400001526e+01 0.0000000000e+00\ninterpolatory covolume: 0.0000000000e+00\ndim = 3\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656244040e+00 -8.4268432856e-01 0.0000000000e+00 0.0000000000e+00 2.8089478612e-01\nsurrogate_pressure = 1.0000003576e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 1.3599999428e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 2.9400001526e+01 0.0000000000e+00 0.0000000000e+00\n\ndouble:\n\ninterpolatory covolume: 0.0000000000e+00\ndim = 1\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819023037e-01\nharten entropy = 1.2938989975e+00\nharten_entropy_derivative = 2.0418242801e+00 -1.2046645839e+00 4.0155486130e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 1.8100000000e+01 3.1983333333e+01\ninterpolatory covolume: 0.0000000000e+00\ndim = 2\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819023037e-01\nharten entropy = 1.2938989975e+00\nharten_entropy_derivative = 2.0418242801e+00 -1.2046645839e+00 0.0000000000e+00 4.0155486130e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 1.8100000000e+01 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 3.1983333333e+01 0.0000000000e+00\ninterpolatory covolume: 0.0000000000e+00\ndim = 3\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819023037e-01\nharten entropy = 1.2938989975e+00\nharten_entropy_derivative = 2.0418242801e+00 -1.2046645839e+00 0.0000000000e+00 0.0000000000e+00 4.0155486130e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00 1.8100000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 3.1983333333e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 0.0000000000e+00\ndim = 1\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819026709e-01\nharten entropy = 1.2938990593e+00\nharten_entropy_derivative = 2.0418241024e+00 -1.2046644688e+00 4.0155482292e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 1.8099998474e+01 3.1983331680e+01\ninterpolatory covolume: 0.0000000000e+00\ndim = 2\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819026709e-01\nharten entropy = 1.2938990593e+00\nharten_entropy_derivative = 2.0418241024e+00 -1.2046644688e+00 0.0000000000e+00 4.0155482292e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 1.8099998474e+01 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 3.1983331680e+01 0.0000000000e+00\ninterpolatory covolume: 0.0000000000e+00\ndim = 3\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819026709e-01\nharten entropy = 1.2938990593e+00\nharten_entropy_derivative = 2.0418241024e+00 -1.2046644688e+00 0.0000000000e+00 0.0000000000e+00 4.0155482292e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00 1.8099998474e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 3.1983331680e+01 0.0000000000e+00 0.0000000000e+00\n\ndouble:\n\ninterpolatory covolume: 1.0000000000e-01\ndim = 1\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694680208e+00\nharten entropy = 1.6435317327e+00\nharten_entropy_derivative = 1.6899437583e+00 -8.2176586633e-01 2.7392195544e-01\nsurrogate_pressure = 1.1627906977e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 1.3762790698e+01 2.9888372093e+01\ninterpolatory covolume: 1.0000000000e-01\ndim = 2\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694680208e+00\nharten entropy = 1.6435317327e+00\nharten_entropy_derivative = 1.6899437583e+00 -8.2176586633e-01 0.0000000000e+00 2.7392195544e-01\nsurrogate_pressure = 1.1627906977e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 1.3762790698e+01 0.0000000000e+00 0.0000000000e+00 1.1627906977e+00 2.9888372093e+01 0.0000000000e+00\ninterpolatory covolume: 1.0000000000e-01\ndim = 3\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694680208e+00\nharten entropy = 1.6435317327e+00\nharten_entropy_derivative = 1.6899437583e+00 -8.2176586633e-01 0.0000000000e+00 0.0000000000e+00 2.7392195544e-01\nsurrogate_pressure = 1.1627906977e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3762790698e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.1627906977e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.1627906977e+00 2.9888372093e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 1.0000000149e-01\ndim = 1\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694685936e+00\nharten entropy = 1.6435320377e+00\nharten_entropy_derivative = 1.6899434328e+00 -8.2176560163e-01 2.7392187715e-01\nsurrogate_pressure = 1.1627911329e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 1.3762790680e+01 2.9888372421e+01\ninterpolatory covolume: 1.0000000149e-01\ndim = 2\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694685936e+00\nharten entropy = 1.6435320377e+00\nharten_entropy_derivative = 1.6899434328e+00 -8.2176560163e-01 0.0000000000e+00 2.7392187715e-01\nsurrogate_pressure = 1.1627911329e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 1.3762790680e+01 0.0000000000e+00 0.0000000000e+00 1.1627911329e+00 2.9888372421e+01 0.0000000000e+00\ninterpolatory covolume: 1.0000000149e-01\ndim = 3\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694685936e+00\nharten entropy = 1.6435320377e+00\nharten_entropy_derivative = 1.6899434328e+00 -8.2176560163e-01 0.0000000000e+00 0.0000000000e+00 2.7392187715e-01\nsurrogate_pressure = 1.1627911329e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 1.3762790680e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.1627911329e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.1627911329e+00 2.9888372421e+01 0.0000000000e+00 0.0000000000e+00\n\ndouble:\n\ninterpolatory covolume: 1.0000000000e-01\ndim = 1\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149520626e-01\nharten entropy = 1.2119903874e+00\nharten_entropy_derivative = 1.8661328096e+00 -1.1284048434e+00 3.7613494781e-01\nsurrogate_pressure = 1.2345679012e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 1.8334567901e+01 3.2687037037e+01\ninterpolatory covolume: 1.0000000000e-01\ndim = 2\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149520626e-01\nharten entropy = 1.2119903874e+00\nharten_entropy_derivative = 1.8661328096e+00 -1.1284048434e+00 0.0000000000e+00 3.7613494781e-01\nsurrogate_pressure = 1.2345679012e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 1.8334567901e+01 0.0000000000e+00 0.0000000000e+00 1.2345679012e+00 3.2687037037e+01 0.0000000000e+00\ninterpolatory covolume: 1.0000000000e-01\ndim = 3\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149520626e-01\nharten entropy = 1.2119903874e+00\nharten_entropy_derivative = 1.8661328096e+00 -1.1284048434e+00 0.0000000000e+00 0.0000000000e+00 3.7613494781e-01\nsurrogate_pressure = 1.2345679012e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00 1.8334567901e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.2345679012e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.2345679012e+00 3.2687037037e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 1.0000000149e-01\ndim = 1\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149522305e-01\nharten entropy = 1.2119904757e+00\nharten_entropy_derivative = 1.8661326170e+00 -1.1284047365e+00 3.7613490224e-01\nsurrogate_pressure = 1.2345678806e+00\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 1.8334568024e+01 3.2687034607e+01\ninterpolatory covolume: 1.0000000149e-01\ndim = 2\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149522305e-01\nharten entropy = 1.2119904757e+00\nharten_entropy_derivative = 1.8661326170e+00 -1.1284047365e+00 0.0000000000e+00 3.7613490224e-01\nsurrogate_pressure = 1.2345678806e+00\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 1.8334568024e+01 0.0000000000e+00 0.0000000000e+00 1.2345678806e+00 3.2687034607e+01 0.0000000000e+00\ninterpolatory covolume: 1.0000000149e-01\ndim = 3\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149522305e-01\nharten entropy = 1.2119904757e+00\nharten_entropy_derivative = 1.8661326170e+00 -1.1284047365e+00 0.0000000000e+00 0.0000000000e+00 3.7613490224e-01\nsurrogate_pressure = 1.2345678806e+00\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00 1.8334568024e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.2345678806e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.2345678806e+00 3.2687034607e+01 0.0000000000e+00 0.0000000000e+00\n\ndouble:\n\ninterpolatory covolume: 5.0000000000e-01\ndim = 1\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429354725e-01\nharten entropy = 1.3789501326e+00\nharten_entropy_derivative = 1.0615727211e+00 -6.8947506630e-01 2.2982502210e-01\nsurrogate_pressure = 3.3333333333e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 1.5933333333e+01 3.6400000000e+01\ninterpolatory covolume: 5.0000000000e-01\ndim = 2\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429354725e-01\nharten entropy = 1.3789501326e+00\nharten_entropy_derivative = 1.0615727211e+00 -6.8947506630e-01 0.0000000000e+00 2.2982502210e-01\nsurrogate_pressure = 3.3333333333e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 1.5933333333e+01 0.0000000000e+00 0.0000000000e+00 3.3333333333e+00 3.6400000000e+01 0.0000000000e+00\ninterpolatory covolume: 5.0000000000e-01\ndim = 3\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429354725e-01\nharten entropy = 1.3789501326e+00\nharten_entropy_derivative = 1.0615727211e+00 -6.8947506630e-01 0.0000000000e+00 0.0000000000e+00 2.2982502210e-01\nsurrogate_pressure = 3.3333333333e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 1.5933333333e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 3.3333333333e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 3.3333333333e+00 3.6400000000e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 5.0000000000e-01\ndim = 1\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429401636e-01\nharten entropy = 1.3789503574e+00\nharten_entropy_derivative = 1.0615724325e+00 -6.8947488070e-01 2.2982497513e-01\nsurrogate_pressure = 3.3333344460e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 1.5933334351e+01 3.6400005341e+01\ninterpolatory covolume: 5.0000000000e-01\ndim = 2\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429401636e-01\nharten entropy = 1.3789503574e+00\nharten_entropy_derivative = 1.0615724325e+00 -6.8947488070e-01 0.0000000000e+00 2.2982497513e-01\nsurrogate_pressure = 3.3333344460e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 1.5933334351e+01 0.0000000000e+00 0.0000000000e+00 3.3333344460e+00 3.6400005341e+01 0.0000000000e+00\ninterpolatory covolume: 5.0000000000e-01\ndim = 3\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429401636e-01\nharten entropy = 1.3789503574e+00\nharten_entropy_derivative = 1.0615724325e+00 -6.8947488070e-01 0.0000000000e+00 0.0000000000e+00 2.2982497513e-01\nsurrogate_pressure = 3.3333344460e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 1.5933334351e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 3.3333344460e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 3.3333344460e+00 3.6400005341e+01 0.0000000000e+00 0.0000000000e+00\n\ndouble:\n\ninterpolatory covolume: 5.0000000000e-01\ndim = 1\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141072431e-02\nharten entropy = 5.1066071232e-01\nharten_entropy_derivative = -7.7896611380e-01 -4.7544273216e-01 1.5848091072e-01\nsurrogate_pressure = 2.0000000000e+01\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 3.7100000000e+01 8.8983333333e+01\ninterpolatory covolume: 5.0000000000e-01\ndim = 2\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141072431e-02\nharten entropy = 5.1066071232e-01\nharten_entropy_derivative = -7.7896611380e-01 -4.7544273216e-01 0.0000000000e+00 1.5848091072e-01\nsurrogate_pressure = 2.0000000000e+01\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 3.7100000000e+01 0.0000000000e+00 0.0000000000e+00 2.0000000000e+01 8.8983333333e+01 0.0000000000e+00\ninterpolatory covolume: 5.0000000000e-01\ndim = 3\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141072431e-02\nharten entropy = 5.1066071232e-01\nharten_entropy_derivative = -7.7896611380e-01 -4.7544273216e-01 0.0000000000e+00 0.0000000000e+00 1.5848091072e-01\nsurrogate_pressure = 2.0000000000e+01\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00 3.7100000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 2.0000000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 2.0000000000e+01 8.8983333333e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 5.0000000000e-01\ndim = 1\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141100839e-02\nharten entropy = 5.1066082716e-01\nharten_entropy_derivative = -7.7896571159e-01 -4.7544255853e-01 1.5848085284e-01\nsurrogate_pressure = 1.9999996185e+01\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 3.7099994659e+01 8.8983314514e+01\ninterpolatory covolume: 5.0000000000e-01\ndim = 2\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141100839e-02\nharten entropy = 5.1066082716e-01\nharten_entropy_derivative = -7.7896571159e-01 -4.7544255853e-01 0.0000000000e+00 1.5848085284e-01\nsurrogate_pressure = 1.9999996185e+01\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 3.7099994659e+01 0.0000000000e+00 0.0000000000e+00 1.9999996185e+01 8.8983314514e+01 0.0000000000e+00\ninterpolatory covolume: 5.0000000000e-01\ndim = 3\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141100839e-02\nharten entropy = 5.1066082716e-01\nharten_entropy_derivative = -7.7896571159e-01 -4.7544255853e-01 0.0000000000e+00 0.0000000000e+00 1.5848085284e-01\nsurrogate_pressure = 1.9999996185e+01\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00 3.7099994659e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.9999996185e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.9999996185e+01 8.8983314514e+01 0.0000000000e+00 0.0000000000e+00\n"
  },
  {
    "path": "tests/euler_aeos/hyperbolic_system.threads=2.output.osx-m1",
    "content": "\ndouble:\n\ninterpolatory covolume: 0.0000000000e+00\ndim = 1\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 2.8089484437e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 1.3600000000e+01 2.9400000000e+01\ninterpolatory covolume: 0.0000000000e+00\ndim = 2\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 0.0000000000e+00 2.8089484437e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 1.3600000000e+01 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 2.9400000000e+01 0.0000000000e+00\ninterpolatory covolume: 0.0000000000e+00\ndim = 3\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 0.0000000000e+00 0.0000000000e+00 2.8089484437e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3600000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 2.9400000000e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 0.0000000000e+00\ndim = 1\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656244040e+00 -8.4268432856e-01 2.8089478612e-01\nsurrogate_pressure = 1.0000003576e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 1.3599999428e+01 2.9400001526e+01\ninterpolatory covolume: 0.0000000000e+00\ndim = 2\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656244040e+00 -8.4268432856e-01 0.0000000000e+00 2.8089478612e-01\nsurrogate_pressure = 1.0000003576e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 1.3599999428e+01 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 2.9400001526e+01 0.0000000000e+00\ninterpolatory covolume: 0.0000000000e+00\ndim = 3\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656244040e+00 -8.4268432856e-01 0.0000000000e+00 0.0000000000e+00 2.8089478612e-01\nsurrogate_pressure = 1.0000003576e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 1.3599999428e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 2.9400001526e+01 0.0000000000e+00 0.0000000000e+00\n\ndouble:\n\ninterpolatory covolume: 0.0000000000e+00\ndim = 1\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819023037e-01\nharten entropy = 1.2938989975e+00\nharten_entropy_derivative = 2.0418242801e+00 -1.2046645839e+00 4.0155486130e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 1.8100000000e+01 3.1983333333e+01\ninterpolatory covolume: 0.0000000000e+00\ndim = 2\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819023037e-01\nharten entropy = 1.2938989975e+00\nharten_entropy_derivative = 2.0418242801e+00 -1.2046645839e+00 0.0000000000e+00 4.0155486130e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 1.8100000000e+01 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 3.1983333333e+01 0.0000000000e+00\ninterpolatory covolume: 0.0000000000e+00\ndim = 3\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819023037e-01\nharten entropy = 1.2938989975e+00\nharten_entropy_derivative = 2.0418242801e+00 -1.2046645839e+00 0.0000000000e+00 0.0000000000e+00 4.0155486130e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00 1.8100000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 3.1983333333e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 0.0000000000e+00\ndim = 1\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111106873e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819014788e-01\nharten entropy = 1.2938989401e+00\nharten_entropy_derivative = 2.0418245792e+00 -1.2046647072e+00 4.0155491233e-01\nsurrogate_pressure = 9.9999958277e-01\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 1.8099998474e+01 3.1983331680e+01\ninterpolatory covolume: 0.0000000000e+00\ndim = 2\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111106873e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819014788e-01\nharten entropy = 1.2938989401e+00\nharten_entropy_derivative = 2.0418245792e+00 -1.2046647072e+00 0.0000000000e+00 4.0155491233e-01\nsurrogate_pressure = 9.9999958277e-01\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 1.8099998474e+01 0.0000000000e+00 0.0000000000e+00 9.9999958277e-01 3.1983331680e+01 0.0000000000e+00\ninterpolatory covolume: 0.0000000000e+00\ndim = 3\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111106873e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819014788e-01\nharten entropy = 1.2938989401e+00\nharten_entropy_derivative = 2.0418245792e+00 -1.2046647072e+00 0.0000000000e+00 0.0000000000e+00 4.0155491233e-01\nsurrogate_pressure = 9.9999958277e-01\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00 1.8099998474e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 9.9999958277e-01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 9.9999958277e-01 3.1983331680e+01 0.0000000000e+00 0.0000000000e+00\n\ndouble:\n\ninterpolatory covolume: 1.0000000000e-01\ndim = 1\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694680208e+00\nharten entropy = 1.6435317327e+00\nharten_entropy_derivative = 1.6899437583e+00 -8.2176586633e-01 2.7392195544e-01\nsurrogate_pressure = 1.1627906977e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 1.3762790698e+01 2.9888372093e+01\ninterpolatory covolume: 1.0000000000e-01\ndim = 2\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694680208e+00\nharten entropy = 1.6435317327e+00\nharten_entropy_derivative = 1.6899437583e+00 -8.2176586633e-01 0.0000000000e+00 2.7392195544e-01\nsurrogate_pressure = 1.1627906977e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 1.3762790698e+01 0.0000000000e+00 0.0000000000e+00 1.1627906977e+00 2.9888372093e+01 0.0000000000e+00\ninterpolatory covolume: 1.0000000000e-01\ndim = 3\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694680208e+00\nharten entropy = 1.6435317327e+00\nharten_entropy_derivative = 1.6899437583e+00 -8.2176586633e-01 0.0000000000e+00 0.0000000000e+00 2.7392195544e-01\nsurrogate_pressure = 1.1627906977e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3762790698e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.1627906977e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.1627906977e+00 2.9888372093e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 1.0000000149e-01\ndim = 1\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694684744e+00\nharten entropy = 1.6435319185e+00\nharten_entropy_derivative = 1.6899434328e+00 -8.2176560163e-01 2.7392187715e-01\nsurrogate_pressure = 1.1627911329e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 1.3762790680e+01 2.9888372421e+01\ninterpolatory covolume: 1.0000000149e-01\ndim = 2\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694684744e+00\nharten entropy = 1.6435319185e+00\nharten_entropy_derivative = 1.6899434328e+00 -8.2176560163e-01 0.0000000000e+00 2.7392187715e-01\nsurrogate_pressure = 1.1627911329e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 1.3762790680e+01 0.0000000000e+00 0.0000000000e+00 1.1627911329e+00 2.9888372421e+01 0.0000000000e+00\ninterpolatory covolume: 1.0000000149e-01\ndim = 3\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694684744e+00\nharten entropy = 1.6435319185e+00\nharten_entropy_derivative = 1.6899434328e+00 -8.2176560163e-01 0.0000000000e+00 0.0000000000e+00 2.7392187715e-01\nsurrogate_pressure = 1.1627911329e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 1.3762790680e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.1627911329e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.1627911329e+00 2.9888372421e+01 0.0000000000e+00 0.0000000000e+00\n\ndouble:\n\ninterpolatory covolume: 1.0000000000e-01\ndim = 1\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149520626e-01\nharten entropy = 1.2119903874e+00\nharten_entropy_derivative = 1.8661328096e+00 -1.1284048434e+00 3.7613494781e-01\nsurrogate_pressure = 1.2345679012e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 1.8334567901e+01 3.2687037037e+01\ninterpolatory covolume: 1.0000000000e-01\ndim = 2\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149520626e-01\nharten entropy = 1.2119903874e+00\nharten_entropy_derivative = 1.8661328096e+00 -1.1284048434e+00 0.0000000000e+00 3.7613494781e-01\nsurrogate_pressure = 1.2345679012e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 1.8334567901e+01 0.0000000000e+00 0.0000000000e+00 1.2345679012e+00 3.2687037037e+01 0.0000000000e+00\ninterpolatory covolume: 1.0000000000e-01\ndim = 3\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149520626e-01\nharten entropy = 1.2119903874e+00\nharten_entropy_derivative = 1.8661328096e+00 -1.1284048434e+00 0.0000000000e+00 0.0000000000e+00 3.7613494781e-01\nsurrogate_pressure = 1.2345679012e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00 1.8334567901e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.2345679012e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.2345679012e+00 3.2687037037e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 1.0000000149e-01\ndim = 1\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111106873e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149513364e-01\nharten entropy = 1.2119903564e+00\nharten_entropy_derivative = 1.8661328554e+00 -1.1284048557e+00 3.7613496184e-01\nsurrogate_pressure = 1.2345674038e+00\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 1.8334566116e+01 3.2687034607e+01\ninterpolatory covolume: 1.0000000149e-01\ndim = 2\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111106873e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149513364e-01\nharten entropy = 1.2119903564e+00\nharten_entropy_derivative = 1.8661328554e+00 -1.1284048557e+00 0.0000000000e+00 3.7613496184e-01\nsurrogate_pressure = 1.2345674038e+00\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 1.8334566116e+01 0.0000000000e+00 0.0000000000e+00 1.2345674038e+00 3.2687034607e+01 0.0000000000e+00\ninterpolatory covolume: 1.0000000149e-01\ndim = 3\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111106873e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149513364e-01\nharten entropy = 1.2119903564e+00\nharten_entropy_derivative = 1.8661328554e+00 -1.1284048557e+00 0.0000000000e+00 0.0000000000e+00 3.7613496184e-01\nsurrogate_pressure = 1.2345674038e+00\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00 1.8334566116e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.2345674038e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.2345674038e+00 3.2687034607e+01 0.0000000000e+00 0.0000000000e+00\n\ndouble:\n\ninterpolatory covolume: 5.0000000000e-01\ndim = 1\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429354725e-01\nharten entropy = 1.3789501326e+00\nharten_entropy_derivative = 1.0615727211e+00 -6.8947506630e-01 2.2982502210e-01\nsurrogate_pressure = 3.3333333333e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 1.5933333333e+01 3.6400000000e+01\ninterpolatory covolume: 5.0000000000e-01\ndim = 2\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429354725e-01\nharten entropy = 1.3789501326e+00\nharten_entropy_derivative = 1.0615727211e+00 -6.8947506630e-01 0.0000000000e+00 2.2982502210e-01\nsurrogate_pressure = 3.3333333333e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 1.5933333333e+01 0.0000000000e+00 0.0000000000e+00 3.3333333333e+00 3.6400000000e+01 0.0000000000e+00\ninterpolatory covolume: 5.0000000000e-01\ndim = 3\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429354725e-01\nharten entropy = 1.3789501326e+00\nharten_entropy_derivative = 1.0615727211e+00 -6.8947506630e-01 0.0000000000e+00 0.0000000000e+00 2.2982502210e-01\nsurrogate_pressure = 3.3333333333e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 1.5933333333e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 3.3333333333e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 3.3333333333e+00 3.6400000000e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 5.0000000000e-01\ndim = 1\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429401636e-01\nharten entropy = 1.3789503574e+00\nharten_entropy_derivative = 1.0615724325e+00 -6.8947488070e-01 2.2982497513e-01\nsurrogate_pressure = 3.3333344460e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 1.5933334351e+01 3.6400005341e+01\ninterpolatory covolume: 5.0000000000e-01\ndim = 2\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429401636e-01\nharten entropy = 1.3789503574e+00\nharten_entropy_derivative = 1.0615724325e+00 -6.8947488070e-01 0.0000000000e+00 2.2982497513e-01\nsurrogate_pressure = 3.3333344460e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 1.5933334351e+01 0.0000000000e+00 0.0000000000e+00 3.3333344460e+00 3.6400005341e+01 0.0000000000e+00\ninterpolatory covolume: 5.0000000000e-01\ndim = 3\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429401636e-01\nharten entropy = 1.3789503574e+00\nharten_entropy_derivative = 1.0615724325e+00 -6.8947488070e-01 0.0000000000e+00 0.0000000000e+00 2.2982497513e-01\nsurrogate_pressure = 3.3333344460e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 1.5933334351e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 3.3333344460e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 3.3333344460e+00 3.6400005341e+01 0.0000000000e+00 0.0000000000e+00\n\ndouble:\n\ninterpolatory covolume: 5.0000000000e-01\ndim = 1\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141072431e-02\nharten entropy = 5.1066071232e-01\nharten_entropy_derivative = -7.7896611380e-01 -4.7544273216e-01 1.5848091072e-01\nsurrogate_pressure = 2.0000000000e+01\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 3.7100000000e+01 8.8983333333e+01\ninterpolatory covolume: 5.0000000000e-01\ndim = 2\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141072431e-02\nharten entropy = 5.1066071232e-01\nharten_entropy_derivative = -7.7896611380e-01 -4.7544273216e-01 0.0000000000e+00 1.5848091072e-01\nsurrogate_pressure = 2.0000000000e+01\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 3.7100000000e+01 0.0000000000e+00 0.0000000000e+00 2.0000000000e+01 8.8983333333e+01 0.0000000000e+00\ninterpolatory covolume: 5.0000000000e-01\ndim = 3\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141072431e-02\nharten entropy = 5.1066071232e-01\nharten_entropy_derivative = -7.7896611380e-01 -4.7544273216e-01 0.0000000000e+00 0.0000000000e+00 1.5848091072e-01\nsurrogate_pressure = 2.0000000000e+01\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00 3.7100000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 2.0000000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 2.0000000000e+01 8.8983333333e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 5.0000000000e-01\ndim = 1\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111106873e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141091526e-02\nharten entropy = 5.1066076756e-01\nharten_entropy_derivative = -7.7896547318e-01 -4.7544273734e-01 1.5848091245e-01\nsurrogate_pressure = 1.9999986649e+01\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 3.7099983215e+01 8.8983291626e+01\ninterpolatory covolume: 5.0000000000e-01\ndim = 2\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111106873e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141091526e-02\nharten entropy = 5.1066076756e-01\nharten_entropy_derivative = -7.7896547318e-01 -4.7544273734e-01 0.0000000000e+00 1.5848091245e-01\nsurrogate_pressure = 1.9999986649e+01\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 3.7099983215e+01 0.0000000000e+00 0.0000000000e+00 1.9999986649e+01 8.8983291626e+01 0.0000000000e+00\ninterpolatory covolume: 5.0000000000e-01\ndim = 3\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111106873e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141091526e-02\nharten entropy = 5.1066076756e-01\nharten_entropy_derivative = -7.7896547318e-01 -4.7544273734e-01 0.0000000000e+00 0.0000000000e+00 1.5848091245e-01\nsurrogate_pressure = 1.9999986649e+01\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00 3.7099983215e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.9999986649e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.9999986649e+01 8.8983291626e+01 0.0000000000e+00 0.0000000000e+00\n"
  },
  {
    "path": "tests/euler_aeos/limiter-NASG.cc",
    "content": "// force distinct symbols in test\n#define EulerAEOS EulerAEOSTest\n\n#include <hyperbolic_system.h>\n#include <multicomponent_vector.h>\n#include <simd.h>\n\n#include <limiter.h>\n\n#define DEBUG_EXPENSIVE_BOUNDS_CHECK\n#define DEBUG_OUTPUT\n#define DEBUG_OUTPUT_LIMITER\n#include <limiter.template.h>\n\nusing namespace ryujin::EulerAEOS;\nusing namespace ryujin;\nusing namespace dealii;\n\nint main()\n{\n  constexpr int dim = 1;\n\n  HyperbolicSystem hyperbolic_system;\n  Limiter<dim, double>::Parameters limiter_parameters;\n\n  const auto set_covolume = [&](const double covolume) {\n    /*\n     * Set the interpolatory covolume by selecting an equation of state\n     * with a covolume.\n     */\n    std::stringstream parameters;\n    parameters << \"subsection HyperbolicSystem\\n\"\n               << \"set equation of state = noble abel stiffened gas\\n\"\n               << \"subsection noble abel stiffened gas\\n\"\n               << \"set covolume b = \" << std::to_string(covolume) << \"\\n\"\n               << \"set reference pressure = 0.1\\n\"\n               << \"set reference specific internal energy = 0.1\\n\"\n               << \"end\\n\"\n               << \"end\\n\"\n               << std::endl;\n    ParameterAcceptor::initialize(parameters);\n  };\n\n  using state_type = HyperbolicSystemView<dim, double>::state_type;\n\n  using bounds_type = Limiter<dim, double>::Bounds;\n\n  static constexpr unsigned int n_precomputed_values =\n      HyperbolicSystemView<dim, double>::n_precomputed_values;\n\n  using precomputed_type =\n      Vectors::MultiComponentVector<double, n_precomputed_values>;\n  precomputed_type dummy;\n\n  Limiter<dim, double> limiter(hyperbolic_system, limiter_parameters, dummy);\n\n  const auto view = hyperbolic_system.template view<dim, double>();\n\n  constexpr double gamma = 1.4;\n\n  const auto test =\n      [&](const state_type &U, const state_type &P, const bounds_type &bounds) {\n        std::cout << \"State: \" << U << \"\\nSpecific entropy: \"\n                  << view.surrogate_specific_entropy(U, gamma)\n                  << \"\\nBounds: \" << bounds[0] << \" \" << bounds[1] << \" \"\n                  << bounds[2] << std::endl;\n\n        const auto &[l, success] = limiter.limit(bounds, U, P);\n\n        std::cout << \"l: \" << l;\n        if (success)\n          std::cout << \"\\nSuccess!\";\n        else\n          std::cout << \"\\nFailure!\";\n        std::cout << std::endl;\n      };\n\n  std::cout << std::setprecision(16);\n  std::cout << std::scientific;\n\n  {\n    std::cout << \"\\n\\nChecking individual limiter components:\" << std::endl;\n\n    std::cout << \"\\nMinimum density bound\" << std::endl;\n    auto U = state_type{{1.0, 1.4, 3.0}};\n    auto P = state_type{{-0.2, 0.1, 0.1}};\n    auto bounds = bounds_type{0.9, 1.1, 2.0, gamma};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMinimum density bound (eps):\" << std::endl;\n    U = state_type{{0.9 + 1.0e-10, 1.4, 3.0}};\n    P = state_type{{-5.0e-10, 0.1, 0.1}};\n    bounds = bounds_type{0.9, 1.1, 2.0, gamma};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMaximum density bound\" << std::endl;\n    U = state_type{{1.0, 1.4, 3.0}};\n    P = state_type{{0.2, 0.1, 0.1}};\n    bounds = bounds_type{0.9, 1.1, 1.0, gamma};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMaximum density bound (eps):\" << std::endl;\n    U = state_type{{1.1 - 1.0e-10, 1.4, 3.0}};\n    P = state_type{{5.0e-10, 0.1, 0.1}};\n    bounds = bounds_type{0.9, 1.1, 1.0, gamma};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMinimum entropy bound\" << std::endl;\n    U = state_type{{1.0, 1.4, 2.8}};\n    P = state_type{{0.1, 0.1, -0.3}};\n    bounds = bounds_type{0.9, 1.1, 1.8, gamma};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMinimum entropy bound (eps):\" << std::endl;\n    U = state_type{{1.0, 1.4, 2.8}};\n    P = state_type{{0.1, 0.1, -4.0e-10}};\n    bounds = bounds_type{0.9, 1.1, 1.82 - 1.e-10, gamma};\n    test(U, P, bounds);\n  }\n\n  {\n    std::cout << \"\\n\\nChecking individual limiter components with covolume:\"\n              << std::endl;\n\n    set_covolume(1.0e-1);\n    std::cout << \"Covolume b = 1.0e-1\" << std::endl;\n\n    std::cout << \"\\nMinimum density bound\" << std::endl;\n    auto U = state_type{{1.0, 1.4, 3.0}};\n    auto P = state_type{{-0.2, 0.1, 0.1}};\n    auto bounds = bounds_type{0.9, 1.1, 1.7, gamma};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMinimum density bound (eps):\" << std::endl;\n    U = state_type{{0.9 + 1.0e-10, 1.4, 3.0}};\n    P = state_type{{-5.0e-10, 0.1, 0.1}};\n    bounds = bounds_type{0.9, 1.1, 2.0, gamma};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMaximum density bound\" << std::endl;\n    U = state_type{{1.0, 1.4, 3.0}};\n    P = state_type{{0.2, 0.1, 0.1}};\n    bounds = bounds_type{0.9, 1.1, 1.0, gamma};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMaximum density bound (eps):\" << std::endl;\n    U = state_type{{1.1 - 1.0e-10, 1.4, 3.0}};\n    P = state_type{{5.0e-10, 0.1, 0.1}};\n    bounds = bounds_type{0.9, 1.1, 1.0, gamma};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMinimum entropy bound\" << std::endl;\n    U = state_type{{1.0, 1.4, 2.8}};\n    P = state_type{{0.1, 0.1, -0.3}};\n    bounds = bounds_type{0.9, 1.1, 1.5, gamma};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMinimum entropy bound (eps):\" << std::endl;\n    U = state_type{{1.0, 1.4, 2.8}};\n    P = state_type{{0.1, 0.1, -4.0e-10}};\n    bounds = bounds_type{0.9, 1.1, 1.7448913582358123 - 1.e-10, gamma};\n    test(U, P, bounds);\n  }\n\n  {\n    std::cout << \"\\n\\nChecking for limiting close to maximal compressibility:\"\n              << std::endl;\n\n    const double b = 0.2;\n    set_covolume(0.2);\n\n    std::cout << \"\\nMaximum density bound:\" << std::endl;\n    double rho_max = 1 / b - 1.0e-6;\n    double rho_limit = (gamma + 1) * rho_max / (gamma - 1 + 2 * b * rho_max);\n    auto U = state_type{{4.5, 1.4, 100000.0}};\n    auto P = state_type{{1.0, 0.1, 0.1}};\n    auto bounds = bounds_type{0.9, rho_limit, 1.6, gamma};\n    test(U, P, bounds);\n\n    rho_max = 1 / b - 1.0e-3;\n    rho_limit = (gamma + 1) * rho_max / (gamma - 1 + 2 * b * rho_max);\n    std::cout << \"\\nMaximum density and entropy bounds:\" << std::endl;\n    U = state_type{{4.5, 1.4, 500.0}};\n    P = state_type{{1.0, 0.1, 0.1}};\n    bounds = bounds_type{0.9, rho_limit, 1.6, gamma};\n    test(U, P, bounds);\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "tests/euler_aeos/limiter-NASG.threads=2.output",
    "content": "\n\nChecking individual limiter components:\n\nMinimum density bound\nState: 1.0000000000000000e+00 1.3999999999999999e+00 3.0000000000000000e+00\nSpecific entropy: 2.0200000000000000e+00\nBounds: 9.0000000000000002e-01 1.1000000000000001e+00 2.0000000000000000e+00\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.4999999999999993\nbreak: t_l and t_r within tolerance\npsi_l:       0.0200000000000449\npsi_r:       0.1406049448670616\nt_l: (  0  ) 0.4999999999999993\nt_r: (  0  ) 0.4999999999999993\nl: 0.4999999999999993\nSuccess!\n\nMinimum density bound (eps):\nState: 0.9000000001000000 1.3999999999999999 3.0000000000000000\nSpecific entropy: 2.2148607358429531\nBounds: 0.9000000000000000 1.1000000000000001 2.0000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.1999999188484877\nbreak: t_l and t_r within tolerance\npsi_l:       0.1668549447528901\npsi_r:       0.1566549490869405\nt_l: (  0  ) 0.1999999188484877\nt_r: (  0  ) 0.1999999188484877\nl: 0.1999999188484877\nSuccess!\n\nMaximum density bound\nState: 1.0000000000000000 1.3999999999999999 3.0000000000000000\nSpecific entropy: 2.0200000000000000\nBounds: 0.9000000000000000 1.1000000000000001 1.0000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.4999999999999998\nbreak: t_l and t_r within tolerance\npsi_l:       1.0200000000000449\npsi_r:       1.0467292569126085\nt_l: (  0  ) 0.4999999999999998\nt_r: (  0  ) 0.4999999999999998\nl: 0.4999999999999998\nSuccess!\n\nMaximum density bound (eps):\nState: 1.0999999999000001 1.3999999999999999 3.0000000000000000\nSpecific entropy: 1.8456338234386600\nBounds: 0.9000000000000000 1.1000000000000001 1.0000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.1999999188484877\nbreak: t_l and t_r within tolerance\npsi_l:       1.0629792568868683\npsi_r:       1.0567792595094574\nt_l: (  0  ) 0.1999999188484877\nt_r: (  0  ) 0.1999999188484877\nl: 0.1999999188484877\nSuccess!\n\nMinimum entropy bound\nState: 1.0000000000000000 1.3999999999999999 2.7999999999999998\nSpecific entropy: 1.8199999999999998\nBounds: 0.9000000000000000 1.1000000000000001 1.8000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 1.0000000000000000\npsi_l:       0.0200000000000402\npsi_r:       -0.6376373375573607\ndpsi_l:      -0.5920000000000001\ndpsi_r:      -0.7236663281943412\nt_l: (  0  ) 0.0336581777653587\nt_r: (  0  ) 0.0336795493578855\npsi_l:       0.0000004347665240\npsi_r:       -0.0000123111333648\ndpsi_l:      -0.5963930884367089\ndpsi_r:      -0.5963958787410997\nt_l: (  1  ) 0.0336589067585305\nt_r: (  1  ) 0.0336589067585308\nl: 0.0336589067585305\nSuccess!\n\nMinimum entropy bound (eps):\nState: 1.0000000000000000 1.3999999999999999 2.7999999999999998\nSpecific entropy: 1.8199999999999998\nBounds: 0.9000000000000000 1.1000000000000001 1.8199999999000001\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 1.0000000000000000\npsi_l:       0.0000000001000402\npsi_r:       -0.3327777527334004\ndpsi_l:      -0.2968000003760001\ndpsi_r:      -0.3691515100712967\nt_l: (  0  ) 0.0000000003370627\nt_r: (  0  ) 0.0000000003375128\nbreak: t_l and t_r within tolerance\npsi_l:       0.0000000000000000\npsi_r:       -0.0000000000001335\nt_l: (  1  ) 0.0000000003370627\nt_r: (  1  ) 0.0000000003375128\nl: 0.0000000003370627\nSuccess!\n\n\nChecking individual limiter components with covolume:\nCovolume b = 1.0e-1\n\nMinimum density bound\nState: 1.0000000000000000 1.3999999999999999 3.0000000000000000\nSpecific entropy: 1.7544786733909543\nBounds: 0.9000000000000000 1.1000000000000001 1.7000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.4999999999999993\nbreak: t_l and t_r within tolerance\npsi_l:       0.0568237014319652\npsi_r:       0.1599228191537817\nt_l: (  0  ) 0.4999999999999993\nt_r: (  0  ) 0.4999999999999993\nl: 0.4999999999999993\nSuccess!\n\nMinimum density bound (eps):\nState: 0.9000000001000000 1.3999999999999999 3.0000000000000000\nSpecific entropy: 1.9308611257630348\nBounds: 0.9000000000000000 1.1000000000000001 2.0000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.1999999188484877\nBounds violation: low-order specific entropy (critical)!\n\t\tPsi left: 0 <= -0.0557555070412939\n\npsi_l:       -0.0557555070412939\npsi_r:       -0.0659555026580312\ndpsi_l:      -0.0499999991730786\ndpsi_r:      -0.0519999983815635\nt_l: (  0  ) 0.0000000000000000\nt_r: (  0  ) 0.0000000000000000\nbreak: t_l and t_r within tolerance\npsi_l:       -0.0557555070412939\npsi_r:       -0.0557555070412939\nt_l: (  1  ) 0.0000000000000000\nt_r: (  1  ) 0.0000000000000000\nBounds violation: high-order specific entropy!\n\t\trho e: 0 <= 1.9111111112320989\n\t\tPsi:   0 <= -0.0557555070412939\n\nl: 0.0000000000000000\nFailure!\n\nMaximum density bound\nState: 1.0000000000000000 1.3999999999999999 3.0000000000000000\nSpecific entropy: 1.7544786733909543\nBounds: 0.9000000000000000 1.1000000000000001 1.0000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.4999999999999998\nbreak: t_l and t_r within tolerance\npsi_l:       0.7869551184894079\npsi_r:       0.7678479780457752\nt_l: (  0  ) 0.4999999999999998\nt_r: (  0  ) 0.4999999999999998\nl: 0.4999999999999998\nSuccess!\n\nMaximum density bound (eps):\nState: 1.0999999999000001 1.3999999999999999 3.0000000000000000\nSpecific entropy: 1.5953658120280545\nBounds: 0.9000000000000000 1.1000000000000001 1.0000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.1999999188484877\nbreak: t_l and t_r within tolerance\npsi_l:       0.7840979780688411\npsi_r:       0.7778979806426241\nt_l: (  0  ) 0.1999999188484877\nt_r: (  0  ) 0.1999999188484877\nl: 0.1999999188484877\nSuccess!\n\nMinimum entropy bound\nState: 1.0000000000000000 1.3999999999999999 2.7999999999999998\nSpecific entropy: 1.5627323702881177\nBounds: 0.9000000000000000 1.1000000000000001 1.5000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 1.0000000000000000\npsi_l:       0.0654326777340870\npsi_r:       -0.5694030329313752\ndpsi_l:      -0.5704497898872319\ndpsi_r:      -0.6996975088652162\nt_l: (  0  ) 0.1132559028719186\nt_r: (  0  ) 0.1133376443652594\npsi_l:       0.0000054363088455\npsi_r:       -0.0000423783250963\ndpsi_l:      -0.5849441194057947\ndpsi_r:      -0.5849545939052536\nt_l: (  1  ) 0.1132651965858496\nt_r: (  1  ) 0.1132651965858498\nl: 0.1132651965858496\nSuccess!\n\nMinimum entropy bound (eps):\nState: 1.0000000000000000 1.3999999999999999 2.7999999999999998\nSpecific entropy: 1.5627323702881177\nBounds: 0.9000000000000000 1.1000000000000001 1.7448913581358123\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 1.0000000000000000\nBounds violation: low-order specific entropy (critical)!\n\t\tPsi left: 0 <= -0.1899999998956592\n\npsi_l:       -0.1899999998956592\npsi_r:       -0.5619254471953601\ndpsi_l:      -0.3328888892633922\ndpsi_r:      -0.4115155749362583\nt_l: (  0  ) 0.0000000000000000\nt_r: (  0  ) 0.0000000000000000\nbreak: t_l and t_r within tolerance\npsi_l:       -0.1899999998956592\npsi_r:       -0.1899999998956592\nt_l: (  1  ) 0.0000000000000000\nt_r: (  1  ) 0.0000000000000000\nBounds violation: high-order specific entropy!\n\t\trho e: 0 <= 1.8199999999999998\n\t\tPsi:   0 <= -0.1899999998956592\n\nl: 0.0000000000000000\nFailure!\n\n\nChecking for limiting close to maximal compressibility:\n\nMaximum density bound:\nState: 4.5000000000000000 1.3999999999999999 100000.0000000000000000\nSpecific entropy: 4847.2959602620130681\nBounds: 0.9000000000000000 4.9999998333333044 1.6000000000000001\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.4999998333333039\nbreak: t_l and t_r within tolerance\npsi_l:       449848.4145844086888246\npsi_r:       425438.3078629508963786\nt_l: (  0  ) 0.4999998333333039\nt_r: (  0  ) 0.4999998333333039\nl: 0.4999998333333039\nSuccess!\n\nMaximum density and entropy bounds:\nState: 4.5000000000000000 1.3999999999999999 500.0000000000000000\nSpecific entropy: 24.2037899543779815\nBounds: 0.9000000000000000 4.9998333055509248 1.6000000000000001\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.4998333055509242\npsi_l:       2098.4145843987539592\npsi_r:       -2207.0105938994288408\ndpsi_l:      301.4427791982724898\ndpsi_r:      -11288579.4503499604761600\nt_l: (  0  ) 0.3515539019070104\nt_r: (  0  ) 0.4996377208110297\npsi_l:       2133.3520113450931603\npsi_r:       -951.2905523860887342\ndpsi_l:      -422.8276814027140063\ndpsi_r:      -3807952.5825715973041952\nt_l: (  1  ) 0.4744490950668295\nt_r: (  1  ) 0.4993874834578974\nl: 0.4744490950668295\nSuccess!\n"
  },
  {
    "path": "tests/euler_aeos/limiter.cc",
    "content": "// force distinct symbols in test\n#define EulerAEOS EulerAEOSTest\n\n#include <hyperbolic_system.h>\n#include <multicomponent_vector.h>\n#include <simd.h>\n\n#include <limiter.h>\n\n#define DEBUG_EXPENSIVE_BOUNDS_CHECK\n#define DEBUG_OUTPUT\n#define DEBUG_OUTPUT_LIMITER\n#include <limiter.template.h>\n\nusing namespace ryujin::EulerAEOS;\nusing namespace ryujin;\nusing namespace dealii;\n\nint main()\n{\n  constexpr int dim = 1;\n\n  HyperbolicSystem hyperbolic_system;\n  Limiter<dim, double>::Parameters limiter_parameters;\n\n  const auto set_covolume = [&](const double covolume) {\n    /*\n     * Set the interpolatory covolume by selecting an equation of state\n     * with a covolume.\n     */\n    std::stringstream parameters;\n    parameters << \"subsection HyperbolicSystem\\n\"\n               << \"set equation of state = van der waals\\n\"\n               << \"subsection van der waals\\n\"\n               << \"set covolume b = \" << std::to_string(covolume) << \"\\n\"\n               << \"end\\n\"\n               << \"end\\n\"\n               << std::endl;\n    ParameterAcceptor::initialize(parameters);\n  };\n\n  using state_type = HyperbolicSystemView<dim, double>::state_type;\n\n  using bounds_type = Limiter<dim, double>::Bounds;\n\n  static constexpr unsigned int n_precomputed_values =\n      HyperbolicSystemView<dim, double>::n_precomputed_values;\n\n  using precomputed_type =\n      Vectors::MultiComponentVector<double, n_precomputed_values>;\n  precomputed_type dummy;\n\n  Limiter<dim, double> limiter(hyperbolic_system, limiter_parameters, dummy);\n\n  const auto view = hyperbolic_system.template view<dim, double>();\n\n  constexpr double gamma = 1.4;\n\n  const auto test =\n      [&](const state_type &U, const state_type &P, const bounds_type &bounds) {\n        std::cout << \"State: \" << U << \"\\nSpecific entropy: \"\n                  << view.surrogate_specific_entropy(U, gamma)\n                  << \"\\nBounds: \" << bounds[0] << \" \" << bounds[1] << \" \"\n                  << bounds[2] << std::endl;\n\n        const auto &[l, success] = limiter.limit(bounds, U, P);\n\n        std::cout << \"l: \" << l;\n        if (success)\n          std::cout << \"\\nSuccess!\";\n        else\n          std::cout << \"\\nFailure!\";\n        std::cout << std::endl;\n      };\n\n  std::cout << std::setprecision(16);\n  std::cout << std::scientific;\n\n  {\n    std::cout << \"\\n\\nChecking exceptional cases:\" << std::endl;\n\n    std::cout << \"\\nMinimum density violation:\" << std::endl;\n    auto U = state_type{{0.8, 1.4, 3.0}};\n    auto P = state_type{{-0.1, 0.1, 0.1}};\n    auto bounds = bounds_type{0.9, 1.1, 2.0, gamma};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMinimum density violation (eps):\" << std::endl;\n    U = state_type{{0.9 - 1.0e-10, 1.4, 3.0}};\n    P = state_type{{-1.0e-20, 0.1, 0.1}};\n    bounds = bounds_type{0.9, 1.1, 2.0, gamma};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMaximum density violation:\" << std::endl;\n    U = state_type{{1.2, 1.4, 3.0}};\n    P = state_type{{0.1, 0.1, 0.1}};\n    bounds = bounds_type{0.9, 1.1, 2.0, gamma};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMaximum density violation (eps):\" << std::endl;\n    U = state_type{{1.1 + 1.0e-10, 1.4, 3.0}};\n    P = state_type{{1.0e-20, 0.1, 0.1}};\n    bounds = bounds_type{0.9, 1.1, 2.0, gamma};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMinimum entropy violation:\" << std::endl;\n    U = state_type{{1.0, 1.4, 2.8}};\n    P = state_type{{0.1, 0.1, -0.1}};\n    bounds = bounds_type{0.9, 1.1, 2.0, gamma};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMinimum entropy violation (eps):\" << std::endl;\n    U = state_type{{1.0, 1.4, 2.8}};\n    P = state_type{{0.1, 0.1, -1.0e-20}};\n    bounds = bounds_type{0.9, 1.1, 1.82 + 1.e-10, gamma};\n    test(U, P, bounds);\n  }\n\n  {\n    std::cout << \"\\n\\nChecking individual limiter components:\" << std::endl;\n\n    std::cout << \"\\nMinimum density bound\" << std::endl;\n    auto U = state_type{{1.0, 1.4, 3.0}};\n    auto P = state_type{{-0.2, 0.1, 0.1}};\n    auto bounds = bounds_type{0.9, 1.1, 2.0, gamma};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMinimum density bound (eps):\" << std::endl;\n    U = state_type{{0.9 + 1.0e-10, 1.4, 3.0}};\n    P = state_type{{-5.0e-10, 0.1, 0.1}};\n    bounds = bounds_type{0.9, 1.1, 2.0, gamma};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMaximum density bound\" << std::endl;\n    U = state_type{{1.0, 1.4, 3.0}};\n    P = state_type{{0.2, 0.1, 0.1}};\n    bounds = bounds_type{0.9, 1.1, 1.0, gamma};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMaximum density bound (eps):\" << std::endl;\n    U = state_type{{1.1 - 1.0e-10, 1.4, 3.0}};\n    P = state_type{{5.0e-10, 0.1, 0.1}};\n    bounds = bounds_type{0.9, 1.1, 1.0, gamma};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMinimum entropy bound\" << std::endl;\n    U = state_type{{1.0, 1.4, 2.8}};\n    P = state_type{{0.1, 0.1, -0.3}};\n    bounds = bounds_type{0.9, 1.1, 1.8, gamma};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMinimum entropy bound (eps):\" << std::endl;\n    U = state_type{{1.0, 1.4, 2.8}};\n    P = state_type{{0.1, 0.1, -4.0e-10}};\n    bounds = bounds_type{0.9, 1.1, 1.82 - 1.e-10, gamma};\n    test(U, P, bounds);\n  }\n\n  {\n    std::cout << \"\\n\\nChecking individual limiter components with covolume:\"\n              << std::endl;\n\n    set_covolume(1.0e-1);\n    std::cout << \"Covolume b = 1.0e-1\" << std::endl;\n\n    std::cout << \"\\nMinimum density bound\" << std::endl;\n    auto U = state_type{{1.0, 1.4, 3.0}};\n    auto P = state_type{{-0.2, 0.1, 0.1}};\n    auto bounds = bounds_type{0.9, 1.1, 2.0, gamma};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMinimum density bound (eps):\" << std::endl;\n    U = state_type{{0.9 + 1.0e-10, 1.4, 3.0}};\n    P = state_type{{-5.0e-10, 0.1, 0.1}};\n    bounds = bounds_type{0.9, 1.1, 2.0, gamma};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMaximum density bound\" << std::endl;\n    U = state_type{{1.0, 1.4, 3.0}};\n    P = state_type{{0.2, 0.1, 0.1}};\n    bounds = bounds_type{0.9, 1.1, 1.0, gamma};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMaximum density bound (eps):\" << std::endl;\n    U = state_type{{1.1 - 1.0e-10, 1.4, 3.0}};\n    P = state_type{{5.0e-10, 0.1, 0.1}};\n    bounds = bounds_type{0.9, 1.1, 1.0, gamma};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMinimum entropy bound\" << std::endl;\n    U = state_type{{1.0, 1.4, 2.8}};\n    P = state_type{{0.1, 0.1, -0.3}};\n    bounds = bounds_type{0.9, 1.1, 1.7, gamma};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMinimum entropy bound (eps):\" << std::endl;\n    U = state_type{{1.0, 1.4, 2.8}};\n    P = state_type{{0.1, 0.1, -4.0e-10}};\n    bounds = bounds_type{0.9, 1.1, 1.7448913582358123 - 1.e-10, gamma};\n    test(U, P, bounds);\n  }\n\n  {\n    std::cout << \"\\n\\nChecking for limiting close to maximal compressibility:\"\n              << std::endl;\n\n    const double b = 0.2;\n    set_covolume(0.2);\n\n    std::cout << \"\\nMaximum density bound:\" << std::endl;\n    double rho_max = 1 / b - 1.0e-6;\n    double rho_limit = (gamma + 1) * rho_max / (gamma - 1 + 2 * b * rho_max);\n    auto U = state_type{{4.5, 1.4, 100000.0}};\n    auto P = state_type{{1.0, 0.1, 0.1}};\n    auto bounds = bounds_type{0.9, rho_limit, 1.6, gamma};\n    test(U, P, bounds);\n\n    rho_max = 1 / b - 1.0e-3;\n    rho_limit = (gamma + 1) * rho_max / (gamma - 1 + 2 * b * rho_max);\n    std::cout << \"\\nMaximum density and entropy bounds:\" << std::endl;\n    U = state_type{{4.5, 1.4, 500.0}};\n    P = state_type{{1.0, 0.1, 0.1}};\n    bounds = bounds_type{0.9, rho_limit, 1.6, gamma};\n    test(U, P, bounds);\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "tests/euler_aeos/limiter.threads=2.output",
    "content": "\n\nChecking exceptional cases:\n\nMinimum density violation:\nState: 8.0000000000000004e-01 1.3999999999999999e+00 3.0000000000000000e+00\nSpecific entropy: 2.4258971015616480e+00\nBounds: 9.0000000000000002e-01 1.1000000000000001e+00 2.0000000000000000e+00\nBounds violation: low-order density (critical)!\n\t\trho min:         0.9000000000000000\n\t\trho min (delta): 0.1000000000000000\n\t\trho:             0.8000000000000000\n\t\trho max (delta): 0.0000000000000000\n\t\trho max:         1.1000000000000001\n\nBounds violation: high-order density!\n\t\trho min:         0.9000000000000000\n\t\trho min (delta): 0.1000000000000000\n\t\trho:             0.8000000000000000\n\t\trho max (delta): 0.0000000000000000\n\t\trho max:         1.1000000000000001\n\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.0000000000000000\nbreak: t_l and t_r within tolerance\npsi_l:       0.2492990670660760\npsi_r:       0.2492990670660760\nt_l: (  0  ) 0.0000000000000000\nt_r: (  0  ) 0.0000000000000000\nl: 0.0000000000000000\nFailure!\n\nMinimum density violation (eps):\nState: 0.8999999999000000 1.3999999999999999 3.0000000000000000\nSpecific entropy: 2.2148607362515862\nBounds: 0.9000000000000000 1.1000000000000001 2.0000000000000000\nBounds violation: low-order density (critical)!\n\t\trho min:         0.9000000000000000\n\t\trho min (delta): 0.0000000001000000\n\t\trho:             0.8999999999000000\n\t\trho max (delta): 0.0000000000000000\n\t\trho max:         1.1000000000000001\n\nBounds violation: high-order density!\n\t\trho min:         0.9000000000000000\n\t\trho min (delta): 0.0000000001000000\n\t\trho:             0.8999999999000000\n\t\trho max (delta): 0.0000000000000000\n\t\trho max:         1.1000000000000001\n\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.0000000000000000\nbreak: t_l and t_r within tolerance\npsi_l:       0.1668549449812342\npsi_r:       0.1668549449812342\nt_l: (  0  ) 0.0000000000000000\nt_r: (  0  ) 0.0000000000000000\nl: 0.0000000000000000\nFailure!\n\nMaximum density violation:\nState: 1.2000000000000000 1.3999999999999999 3.0000000000000000\nSpecific entropy: 1.6914777945209194\nBounds: 0.9000000000000000 1.1000000000000001 2.0000000000000000\nBounds violation: low-order density (critical)!\n\t\trho min:         0.9000000000000000\n\t\trho min (delta): 0.0000000000000000\n\t\trho:             1.2000000000000000\n\t\trho max (delta): 0.0999999999999999\n\t\trho max:         1.1000000000000001\n\nBounds violation: high-order density!\n\t\trho min:         0.9000000000000000\n\t\trho min (delta): 0.0000000000000000\n\t\trho:             1.2000000000000000\n\t\trho max (delta): 0.0999999999999999\n\t\trho max:         1.1000000000000001\n\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.0000000000000000\nBounds violation: low-order specific entropy (critical)!\n\t\tPsi left: 0 <= -0.4778828199657434\n\nbreak: t_l and t_r within tolerance\npsi_l:       -0.4778828199657434\npsi_r:       -0.4778828199657434\nt_l: (  0  ) 0.0000000000000000\nt_r: (  0  ) 0.0000000000000000\nBounds violation: high-order specific entropy!\n\t\trho e: 0 <= 2.1833333333333336\n\t\tPsi:   0 <= -0.4778828199657434\n\nl: 0.0000000000000000\nFailure!\n\nMaximum density violation (eps):\nState: 1.1000000001000001 1.3999999999999999 3.0000000000000000\nSpecific entropy: 1.8456338231106115\nBounds: 0.9000000000000000 1.1000000000000001 2.0000000000000000\nBounds violation: low-order density (critical)!\n\t\trho min:         0.9000000000000000\n\t\trho min (delta): 0.0000000000000000\n\t\trho:             1.1000000001000001\n\t\trho max (delta): 0.0000000001000000\n\t\trho max:         1.1000000000000001\n\nBounds violation: high-order density!\n\t\trho min:         0.9000000000000000\n\t\trho min (delta): 0.0000000000000000\n\t\trho:             1.1000000001000001\n\t\trho max (delta): 0.0000000001000000\n\t\trho max:         1.1000000000000001\n\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.0000000000000000\nBounds violation: low-order specific entropy (critical)!\n\t\tPsi left: 0 <= -0.1940414864233516\n\nbreak: t_l and t_r within tolerance\npsi_l:       -0.1940414864233516\npsi_r:       -0.1940414864233516\nt_l: (  0  ) 0.0000000000000000\nt_r: (  0  ) 0.0000000000000000\nBounds violation: high-order specific entropy!\n\t\trho e: 0 <= 2.1090909091719010\n\t\tPsi:   0 <= -0.1940414864233516\n\nl: 0.0000000000000000\nFailure!\n\nMinimum entropy violation:\nState: 1.0000000000000000 1.3999999999999999 2.7999999999999998\nSpecific entropy: 1.8199999999999998\nBounds: 0.9000000000000000 1.1000000000000001 2.0000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 1.0000000000000000\nBounds violation: low-order specific entropy (critical)!\n\t\tPsi left: 0 <= -0.1799999999999597\n\npsi_l:       -0.1799999999999597\npsi_r:       -0.6690414861748446\ndpsi_l:      -0.4400000000000000\ndpsi_r:      -0.5385181424381568\nt_l: (  0  ) 0.0000000000000000\nt_r: (  0  ) 0.0000000000000000\nbreak: t_l and t_r within tolerance\npsi_l:       -0.1799999999999597\npsi_r:       -0.1799999999999597\nt_l: (  1  ) 0.0000000000000000\nt_r: (  1  ) 0.0000000000000000\nBounds violation: high-order specific entropy!\n\t\trho e: 0 <= 1.8199999999999998\n\t\tPsi:   0 <= -0.1799999999999597\n\nl: 0.0000000000000000\nFailure!\n\nMinimum entropy violation (eps):\nState: 1.0000000000000000 1.3999999999999999 2.7999999999999998\nSpecific entropy: 1.8199999999999998\nBounds: 0.9000000000000000 1.1000000000000001 1.8200000001000001\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 1.0000000000000000\nBounds violation: low-order specific entropy (critical)!\n\t\tPsi left: 0 <= -0.0000000000999598\n\npsi_l:       -0.0000000000999598\npsi_r:       -0.3327777525448046\ndpsi_l:      -0.2968000000240001\ndpsi_r:      -0.3691515096461485\nt_l: (  0  ) 0.0000000000000000\nt_r: (  0  ) 0.0000000000000000\nbreak: t_l and t_r within tolerance\npsi_l:       -0.0000000000999598\npsi_r:       -0.0000000000999598\nt_l: (  1  ) 0.0000000000000000\nt_r: (  1  ) 0.0000000000000000\nBounds violation: high-order specific entropy!\n\t\trho e: 0 <= 1.8199999999999998\n\t\tPsi:   0 <= -0.0000000000999598\n\nl: 0.0000000000000000\nFailure!\n\n\nChecking individual limiter components:\n\nMinimum density bound\nState: 1.0000000000000000 1.3999999999999999 3.0000000000000000\nSpecific entropy: 2.0200000000000000\nBounds: 0.9000000000000000 1.1000000000000001 2.0000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.4999999999999993\nbreak: t_l and t_r within tolerance\npsi_l:       0.0200000000000449\npsi_r:       0.1406049448670616\nt_l: (  0  ) 0.4999999999999993\nt_r: (  0  ) 0.4999999999999993\nl: 0.4999999999999993\nSuccess!\n\nMinimum density bound (eps):\nState: 0.9000000001000000 1.3999999999999999 3.0000000000000000\nSpecific entropy: 2.2148607358429531\nBounds: 0.9000000000000000 1.1000000000000001 2.0000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.1999999188484877\nbreak: t_l and t_r within tolerance\npsi_l:       0.1668549447528901\npsi_r:       0.1566549490869405\nt_l: (  0  ) 0.1999999188484877\nt_r: (  0  ) 0.1999999188484877\nl: 0.1999999188484877\nSuccess!\n\nMaximum density bound\nState: 1.0000000000000000 1.3999999999999999 3.0000000000000000\nSpecific entropy: 2.0200000000000000\nBounds: 0.9000000000000000 1.1000000000000001 1.0000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.4999999999999998\nbreak: t_l and t_r within tolerance\npsi_l:       1.0200000000000449\npsi_r:       1.0467292569126085\nt_l: (  0  ) 0.4999999999999998\nt_r: (  0  ) 0.4999999999999998\nl: 0.4999999999999998\nSuccess!\n\nMaximum density bound (eps):\nState: 1.0999999999000001 1.3999999999999999 3.0000000000000000\nSpecific entropy: 1.8456338234386600\nBounds: 0.9000000000000000 1.1000000000000001 1.0000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.1999999188484877\nbreak: t_l and t_r within tolerance\npsi_l:       1.0629792568868683\npsi_r:       1.0567792595094574\nt_l: (  0  ) 0.1999999188484877\nt_r: (  0  ) 0.1999999188484877\nl: 0.1999999188484877\nSuccess!\n\nMinimum entropy bound\nState: 1.0000000000000000 1.3999999999999999 2.7999999999999998\nSpecific entropy: 1.8199999999999998\nBounds: 0.9000000000000000 1.1000000000000001 1.8000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 1.0000000000000000\npsi_l:       0.0200000000000402\npsi_r:       -0.6376373375573607\ndpsi_l:      -0.5920000000000001\ndpsi_r:      -0.7236663281943412\nt_l: (  0  ) 0.0336581777653587\nt_r: (  0  ) 0.0336795493578855\npsi_l:       0.0000004347665240\npsi_r:       -0.0000123111333648\ndpsi_l:      -0.5963930884367089\ndpsi_r:      -0.5963958787410997\nt_l: (  1  ) 0.0336589067585305\nt_r: (  1  ) 0.0336589067585308\nl: 0.0336589067585305\nSuccess!\n\nMinimum entropy bound (eps):\nState: 1.0000000000000000 1.3999999999999999 2.7999999999999998\nSpecific entropy: 1.8199999999999998\nBounds: 0.9000000000000000 1.1000000000000001 1.8199999999000001\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 1.0000000000000000\npsi_l:       0.0000000001000402\npsi_r:       -0.3327777527334004\ndpsi_l:      -0.2968000003760001\ndpsi_r:      -0.3691515100712967\nt_l: (  0  ) 0.0000000003370627\nt_r: (  0  ) 0.0000000003375128\nbreak: t_l and t_r within tolerance\npsi_l:       0.0000000000000000\npsi_r:       -0.0000000000001335\nt_l: (  1  ) 0.0000000003370627\nt_r: (  1  ) 0.0000000003375128\nl: 0.0000000003370627\nSuccess!\n\n\nChecking individual limiter components with covolume:\nCovolume b = 1.0e-1\n\nMinimum density bound\nState: 1.0000000000000000 1.3999999999999999 3.0000000000000000\nSpecific entropy: 1.9366376613386493\nBounds: 0.9000000000000000 1.1000000000000001 2.0000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.4999999999999993\nBounds violation: low-order specific entropy (critical)!\n\t\tPsi left: 0 <= -0.0660897630212203\n\nbreak: t_l and t_r within tolerance\npsi_l:       -0.0660897630212203\npsi_r:       0.0808944931220936\nt_l: (  0  ) 0.4999999999999993\nt_r: (  0  ) 0.4999999999999993\nl: 0.4999999999999993\nFailure!\n\nMinimum density bound (eps):\nState: 0.9000000001000000 1.3999999999999999 3.0000000000000000\nSpecific entropy: 2.1328631021178479\nBounds: 0.9000000000000000 1.1000000000000001 2.0000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.1999999188484877\nbreak: t_l and t_r within tolerance\npsi_l:       0.1071444929849098\npsi_r:       0.0969444973419725\nt_l: (  0  ) 0.1999999188484877\nt_r: (  0  ) 0.1999999188484877\nl: 0.1999999188484877\nSuccess!\n\nMaximum density bound\nState: 1.0000000000000000 1.3999999999999999 3.0000000000000000\nSpecific entropy: 1.9366376613386493\nBounds: 0.9000000000000000 1.1000000000000001 1.0000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.4999999999999998\nbreak: t_l and t_r within tolerance\npsi_l:       0.9769551184894123\npsi_r:       0.9867479780457802\nt_l: (  0  ) 0.4999999999999998\nt_r: (  0  ) 0.4999999999999998\nl: 0.4999999999999998\nSuccess!\n\nMaximum density bound (eps):\nState: 1.0999999999000001 1.3999999999999999 3.0000000000000000\nSpecific entropy: 1.7615766426645352\nBounds: 0.9000000000000000 1.1000000000000001 1.0000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.1999999188484877\nbreak: t_l and t_r within tolerance\npsi_l:       1.0029979780390461\npsi_r:       0.9967979806426290\nt_l: (  0  ) 0.1999999188484877\nt_r: (  0  ) 0.1999999188484877\nl: 0.1999999188484877\nSuccess!\n\nMinimum entropy bound\nState: 1.0000000000000000 1.3999999999999999 2.7999999999999998\nSpecific entropy: 1.7448913582358123\nBounds: 0.9000000000000000 1.1000000000000001 1.7000000000000000\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 1.0000000000000000\npsi_l:       0.0468237014319650\npsi_r:       -0.6139034373222245\ndpsi_l:      -0.5934430952055295\ndpsi_r:      -0.7285505100472450\nt_l: (  0  ) 0.0782082675269480\nt_r: (  0  ) 0.0782726916993062\npsi_l:       0.0000030546320721\npsi_r:       -0.0000358509294143\ndpsi_l:      -0.6038926465212701\ndpsi_r:      -0.6039012625870442\nt_l: (  1  ) 0.0782133257610210\nt_r: (  1  ) 0.0782133257610211\nl: 0.0782133257610210\nSuccess!\n\nMinimum entropy bound (eps):\nState: 1.0000000000000000 1.3999999999999999 2.7999999999999998\nSpecific entropy: 1.7448913582358123\nBounds: 0.9000000000000000 1.1000000000000001 1.7448913581358123\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 1.0000000000000000\npsi_l:       0.0000000001043452\npsi_r:       -0.3430254471953551\ndpsi_l:      -0.3048888892633922\ndpsi_r:      -0.3817155749362584\nt_l: (  0  ) 0.0000000003422401\nt_r: (  0  ) 0.0000000003428626\nbreak: t_l and t_r within tolerance\npsi_l:       -0.0000000000000001\npsi_r:       -0.0000000000001903\nt_l: (  1  ) 0.0000000003422401\nt_r: (  1  ) 0.0000000003428626\nl: 0.0000000003422401\nSuccess!\n\n\nChecking for limiting close to maximal compressibility:\n\nMaximum density bound:\nState: 4.5000000000000000 1.3999999999999999 100000.0000000000000000\nSpecific entropy: 4847.3182579745589464\nBounds: 0.9000000000000000 4.9999998333333044 1.6000000000000001\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.4999998333333039\nbreak: t_l and t_r within tolerance\npsi_l:       449850.4845844086376019\npsi_r:       425440.8078628008952364\nt_l: (  0  ) 0.4999998333333039\nt_r: (  0  ) 0.4999998333333039\nl: 0.4999998333333039\nSuccess!\n\nMaximum density and entropy bounds:\nState: 4.5000000000000000 1.3999999999999999 500.0000000000000000\nSpecific entropy: 24.2260876669241298\nBounds: 0.9000000000000000 4.9998333055509248 1.6000000000000001\n\nt_l: (start) 0.0000000000000000\nt_r: (start) 0.4998333055509242\npsi_l:       2100.4845843987541230\npsi_r:       -2204.5107439222101675\ndpsi_l:      302.2627791982724830\ndpsi_r:      -11288578.5503766313195229\nt_l: (  0  ) 0.3517473482289901\nt_r: (  0  ) 0.4996379424181276\npsi_l:       2135.6383949413966548\npsi_r:       -949.6351090434968683\ndpsi_l:      -423.5326553474437219\ndpsi_r:      -3811214.9662162354215980\nt_l: (  1  ) 0.4745359943157750\nt_r: (  1  ) 0.4993883549044513\nl: 0.4745359943157750\nSuccess!\n"
  },
  {
    "path": "tests/euler_aeos/riemann_solver-strict-NASG.cc",
    "content": "// force distinct symbols in test\n#define EulerAEOS EulerAEOSTest\n\n#include <hyperbolic_system.h>\n#include <multicomponent_vector.h>\n#define DEBUG_RIEMANN_SOLVER\n#include <riemann_solver.h>\n#include <riemann_solver.template.h>\n#include <simd.h>\n\n/*\n * Test the NASG interpolation for the EulerAEOS::RiemmanSolver. Do this by\n * simply setting a reference pressure pinfty.\n */\n\nusing namespace ryujin::EulerAEOS;\nusing namespace ryujin;\nusing namespace dealii;\n\nint main()\n{\n  constexpr int dim = 1;\n\n  HyperbolicSystem hyperbolic_system;\n  RiemannSolver<dim, double>::Parameters riemann_solver_parameters;\n\n  static constexpr unsigned int n_precomputed_values =\n      HyperbolicSystemView<dim, double>::n_precomputed_values;\n  using precomputed_type =\n      Vectors::MultiComponentVector<double, n_precomputed_values>;\n  precomputed_type dummy;\n\n  RiemannSolver<dim> riemann_solver(\n      hyperbolic_system, riemann_solver_parameters, dummy);\n\n  std::stringstream parameters;\n  parameters << \"subsection HyperbolicSystem\\n\"\n             << \"set compute strict bounds = true\\n\"\n             << \"end\" << std::endl;\n  ParameterAcceptor::initialize(parameters);\n\n  const auto riemann_data = [&](const auto &state) {\n    const double rho = state[0];\n    const double u = state[1];\n    const double p = state[2];\n    const double gamma = state[3];\n\n    std::array<double, 5> result;\n    result[0] = rho;\n    result[1] = u;\n    result[2] = p;\n    result[3] = gamma;\n    const double interpolation_b =\n        hyperbolic_system.view<dim, double>().eos_interpolation_b();\n    const double x = 1. - interpolation_b * rho;\n    result[4] = std::sqrt(gamma * p / (rho * x));\n    return result;\n  };\n\n  const auto test = [&](const std::array<double, 4> &U_i,\n                        const std::array<double, 4> &U_j) {\n    std::cout << std::endl;\n    std::cout << U_i[0] << \" \" << U_i[1] << \" \" << U_i[2] << \" \" << U_i[3]\n              << std::endl;\n    std::cout << U_j[0] << \" \" << U_j[1] << \" \" << U_j[2] << \" \" << U_j[3]\n              << std::endl;\n    const auto rd_i = riemann_data(U_i);\n    const auto rd_j = riemann_data(U_j);\n    const auto lambda_max = riemann_solver.compute(rd_i, rd_j);\n    std::cout << lambda_max << std::endl;\n  };\n\n  const auto set_covolume = [&](const double covolume) {\n    /*\n     * Set the interpolatory covolume by selecting an equation of state\n     * with a covolume.\n     */\n    std::stringstream parameters;\n    parameters << \"subsection HyperbolicSystem\\n\"\n               << \"set equation of state = noble abel stiffened gas\\n\"\n               << \"subsection noble abel stiffened gas\\n\"\n               << \"set covolume b = \" << std::to_string(covolume) << \"\\n\"\n               << \"set reference pressure = 0.5\\n\"\n               << \"end\\n\"\n               << \"end\\n\"\n               << std::endl;\n    ParameterAcceptor::initialize(parameters);\n  };\n\n  std::cout << std::setprecision(16);\n  std::cout << std::scientific;\n\n  /*\n   * Test vectors for ideal gas with gamma = 1.4:\n   */\n\n  /* Leblanc:*/\n  test({1., 0., 2. / 30., 7. / 5.}, {1.e-3, 0., 2. / 3. * 1.e-10, 7. / 5.});\n  /* Sod:*/\n  test({1., 0., 1., 7. / 5.}, {0.125, 0., 0.1, 7. / 5.});\n  /* Lax:*/\n  test({0.445, 0.698, 3.528, 7. / 5.}, {0.5, 0., 0.571, 7. / 5.});\n  /* Fast shock case 1 (paper, section 5.2): */\n  test({1., 1.e1, 1.e3, 7. / 5.}, {1., 10., 0.01, 7. / 5.});\n  /* Fast shock case 2 (paper, section 5.2): */\n  test({5.99924, 19.5975, 460.894, 7. / 5.},\n       {5.99242, -6.19633, 46.0950, 7. / 5.});\n  /* Fast expansion and slow shock, case 1 (Paper, section 5.1) */\n  test({1., 0., 0.01, 7. / 5.}, {1., 0., 1.e2, 7. / 5.});\n  /* Fast expansion and slow shock, case 2 (Paper, section 5.1) */\n  test({1., -1., 0.01, 7. / 5.}, {1., -1., 1.e2, 7. / 5.});\n  /* Fast expansion and slow shock, case 3 (Paper, section 5.1) */\n  test({1., -2.18, 0.01, 7. / 5.}, {1., -2.18, 100., 7. / 5.});\n  /* Case 9:*/\n  test({1.0e-2, 0., 1.0e-2, 7. / 5.}, {1.e3, 0., 1.e3, 7. / 5.});\n  /* Case 10:*/\n  test({1.0, 2.18, 1.e2, 7. / 5.}, {1.0, 2.18, 0.01, 7. / 5.});\n\n  /*\n   * States with non-constant gamma values:\n   */\n\n  /* Shock-shock */\n  set_covolume(0.003);\n  test({1.5, 100., 22., 2.0041781532448066}, {7., 0., 12., 5.7237635705670113});\n\n  /* Shock-expansion */\n  set_covolume(0.003);\n  test({1.5, 0., 22., 2.0041781532448066}, {7., 0., 12., 5.7237635705670113});\n\n  /* Mie-Gruneisen: shock-expansion 1, gamma_min != gamma_m */\n  set_covolume(0.);\n  test({3500., 20., 2.3e10, 118.01508858712090},\n       {2400., 0., 1.5e11, 2.8761770391786854});\n\n  /* Mie-Gruneisen: shock-expansion 2, gamma_min = gamma_m */\n  set_covolume(0.);\n  test({3500., 20., 2.3e10, 118.01508858712090},\n       {3300., 0., 2.2e10, 8.2392709087064375});\n\n  /* Mie-Gruneisen: shock-expansion 3, gamma_min = gamma_m */\n  set_covolume(0.);\n  test({3500., 20., 2.3e10, 118.01508858712090},\n       {3., 0., 2.2e6, 1.0453481734270629});\n\n  /* Mie-Gruneisen: shock-expansion 4, gamma_min = gamma_m */\n  set_covolume(0.);\n  test({350., 20., 2.3e5, 1.0000474957444776},\n       {3., 0., 2.2e6, 1.0453481734270629});\n\n  /* Mie-Gruneisen: shock-expansion 5, gamma_min != gamma_m */\n  set_covolume(0.);\n  test({15., 20., 7.3e8, 2.2145329586703819},\n       {500., 0., 2.2e9, 1.2899388697970200});\n\n  /*\n   * States with crazy gamma values:\n   */\n\n  test({1., 0., 2. / 30., 2.99}, {1.e-3, 0., 2. / 3. * 1.e-10, 1.40});\n\n  test({1., 0., 2. / 30., 1.01}, {1.e-3, 0., 2. / 3. * 1.e-10, 1.40});\n\n  test({1., 0., 2. / 30., 2.96}, {1.e-3, 0., 2. / 3. * 1.e-10, 2.99});\n\n  test({1., 0., 2. / 30., 40.0}, {1.e-3, 0., 2. / 3. * 1.e-10, 1.001});\n\n  return 0;\n}\n"
  },
  {
    "path": "tests/euler_aeos/riemann_solver-strict-NASG.threads=2.output",
    "content": "\n1.0000000000000000e+00 0.0000000000000000e+00 6.6666666666666666e-02 1.3999999999999999e+00\n1.0000000000000000e-03 0.0000000000000000e+00 6.6666666666666669e-11 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 6.6666666666666666e-02\ngamma_left: 1.3999999999999999e+00\na_left: 3.0550504633038933e-01\nrho_right: 1.0000000000000000e-03\nu_right: 0.0000000000000000e+00\np_right: 6.6666666666666669e-11\ngamma_right: 1.3999999999999999e+00\na_right: 3.0550504633038936e-04\nRS p_1_tilde  = 5.8723550288294901e-02\nRS p_2_tilde  = 5.8723550288294901e-02\nSS p_1_tilde  = 5.8723550288294839e-02\nSS p_2_tilde  = 1.8962827388271815e-03\n   p^*_tilde  = 5.8723550288294901e-02\n   phi(p_*_t) = 6.9680132444205416e+00\n-> lambda_max = 8.3945375309952137e+00\n8.3945375309952137e+00\n\n1.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e+00 1.3999999999999999e+00\n1.2500000000000000e-01 0.0000000000000000e+00 1.0000000000000001e-01 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 1.0000000000000000e+00\ngamma_left: 1.3999999999999999e+00\na_left: 1.1832159566199232e+00\nrho_right: 1.2500000000000000e-01\nu_right: 0.0000000000000000e+00\np_right: 1.0000000000000001e-01\ngamma_right: 1.3999999999999999e+00\na_right: 1.0583005244258361e+00\nRS p_1_tilde  = 3.0676664667059678e-01\nRS p_2_tilde  = 3.0676664667059678e-01\nSS p_1_tilde  = 3.0676664667059667e-01\nSS p_2_tilde  = 3.2333029409004022e-01\n   p^*_tilde  = 3.0676664667059678e-01\n   phi(p_*_t) = 1.9786963438474081e-02\n-> lambda_max = 1.7620896140769142e+00\n1.7620896140769142e+00\n\n4.4500000000000001e-01 6.9799999999999995e-01 3.5280000000000000e+00 1.3999999999999999e+00\n5.0000000000000000e-01 0.0000000000000000e+00 5.7099999999999995e-01 1.3999999999999999e+00\nrho_left: 4.4500000000000001e-01\nu_left: 6.9799999999999995e-01\np_left: 3.5280000000000000e+00\ngamma_left: 1.3999999999999999e+00\na_left: 3.3315650740600322e+00\nrho_right: 5.0000000000000000e-01\nu_right: 0.0000000000000000e+00\np_right: 5.7099999999999995e-01\ngamma_right: 1.3999999999999999e+00\na_right: 1.2644366334458994e+00\nRS p_1_tilde  = 2.5096631320093397e+00\nRS p_2_tilde  = 2.5096631320093397e+00\nSS p_1_tilde  = 2.5096631320093410e+00\nSS p_2_tilde  = 2.4778604535394879e+00\n   p^*_tilde  = 2.5096631320093397e+00\n   phi(p_*_t) = 6.1653197162831685e-02\n-> lambda_max = 2.6335650740600323e+00\n2.6335650740600323e+00\n\n1.0000000000000000e+00 1.0000000000000000e+01 1.0000000000000000e+03 1.3999999999999999e+00\n1.0000000000000000e+00 1.0000000000000000e+01 1.0000000000000000e-02 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 1.0000000000000000e+01\np_left: 1.0000000000000000e+03\ngamma_left: 1.3999999999999999e+00\na_left: 3.7416573867739416e+01\nrho_right: 1.0000000000000000e+00\nu_right: 1.0000000000000000e+01\np_right: 1.0000000000000000e-02\ngamma_right: 1.3999999999999999e+00\na_right: 1.1832159566199231e-01\nRS p_1_tilde  = 9.1244932710571970e+02\nRS p_2_tilde  = 9.1244932710571970e+02\nSS p_1_tilde  = 9.1244932710571993e+02\nSS p_2_tilde  = 4.8074609902285869e+02\n   p^*_tilde  = 9.1244932710571970e+02\n   phi(p_*_t) = 2.5141795977374755e+01\n-> lambda_max = 4.3089895625807941e+01\n4.3089895625807941e+01\n\n5.9992400000000004e+00 1.9597500000000000e+01 4.6089400000000001e+02 1.3999999999999999e+00\n5.9924200000000001e+00 -6.1963299999999997e+00 4.6094999999999999e+01 1.3999999999999999e+00\nrho_left: 5.9992400000000004e+00\nu_left: 1.9597500000000000e+01\np_left: 4.6089400000000001e+02\ngamma_left: 1.3999999999999999e+00\na_left: 1.0370899528836672e+01\nrho_right: 5.9924200000000001e+00\nu_right: -6.1963299999999997e+00\np_right: 4.6094999999999999e+01\ngamma_right: 1.3999999999999999e+00\na_right: 3.2816314493370298e+00\nRS p_1_tilde  = 2.3226554570159969e+03\nRS p_2_tilde  = 2.3226554570159969e+03\nSS p_1_tilde  = 2.3226554570159974e+03\nSS p_2_tilde  = 1.7599311105107934e+03\n   p^*_tilde  = 1.7599311105107934e+03\n   phi(p_*_t) = 7.0445120003856943e-01\n-> lambda_max = 1.2617757915202827e+01\n1.2617757915202827e+01\n\n1.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e-02 1.3999999999999999e+00\n1.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e+02 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 1.0000000000000000e-02\ngamma_left: 1.3999999999999999e+00\na_left: 1.1832159566199231e-01\nrho_right: 1.0000000000000000e+00\nu_right: 0.0000000000000000e+00\np_right: 1.0000000000000000e+02\ngamma_right: 1.3999999999999999e+00\na_right: 1.1832159566199232e+01\nRS p_1_tilde  = 8.2983069275580775e+01\nRS p_2_tilde  = 8.2983069275580775e+01\nSS p_1_tilde  = 8.2983069275580775e+01\nSS p_2_tilde  = 4.8079470435532315e+01\n   p^*_tilde  = 8.2983069275580775e+01\n   phi(p_*_t) = 6.7590391906237013e+00\n-> lambda_max = 1.1832159566199232e+01\n1.1832159566199232e+01\n\n1.0000000000000000e+00 -1.0000000000000000e+00 1.0000000000000000e-02 1.3999999999999999e+00\n1.0000000000000000e+00 -1.0000000000000000e+00 1.0000000000000000e+02 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: -1.0000000000000000e+00\np_left: 1.0000000000000000e-02\ngamma_left: 1.3999999999999999e+00\na_left: 1.1832159566199231e-01\nrho_right: 1.0000000000000000e+00\nu_right: -1.0000000000000000e+00\np_right: 1.0000000000000000e+02\ngamma_right: 1.3999999999999999e+00\na_right: 1.1832159566199232e+01\nRS p_1_tilde  = 8.2983069275580775e+01\nRS p_2_tilde  = 8.2983069275580775e+01\nSS p_1_tilde  = 8.2983069275580775e+01\nSS p_2_tilde  = 4.8079470435532315e+01\n   p^*_tilde  = 8.2983069275580775e+01\n   phi(p_*_t) = 6.7590391906237013e+00\n-> lambda_max = 1.0979062237038955e+01\n1.0979062237038955e+01\n\n1.0000000000000000e+00 -2.1800000000000002e+00 1.0000000000000000e-02 1.3999999999999999e+00\n1.0000000000000000e+00 -2.1800000000000002e+00 1.0000000000000000e+02 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: -2.1800000000000002e+00\np_left: 1.0000000000000000e-02\ngamma_left: 1.3999999999999999e+00\na_left: 1.1832159566199231e-01\nrho_right: 1.0000000000000000e+00\nu_right: -2.1800000000000002e+00\np_right: 1.0000000000000000e+02\ngamma_right: 1.3999999999999999e+00\na_right: 1.1832159566199232e+01\nRS p_1_tilde  = 8.2983069275580775e+01\nRS p_2_tilde  = 8.2983069275580775e+01\nSS p_1_tilde  = 8.2983069275580775e+01\nSS p_2_tilde  = 4.8079470435532315e+01\n   p^*_tilde  = 8.2983069275580775e+01\n   phi(p_*_t) = 6.7590391906237013e+00\n-> lambda_max = 1.2159062237038954e+01\n1.2159062237038954e+01\n\n1.0000000000000000e-02 0.0000000000000000e+00 1.0000000000000000e-02 1.3999999999999999e+00\n1.0000000000000000e+03 0.0000000000000000e+00 1.0000000000000000e+03 1.3999999999999999e+00\nrho_left: 1.0000000000000000e-02\nu_left: 0.0000000000000000e+00\np_left: 1.0000000000000000e-02\ngamma_left: 1.3999999999999999e+00\na_left: 1.1832159566199232e+00\nrho_right: 1.0000000000000000e+03\nu_right: 0.0000000000000000e+00\np_right: 1.0000000000000000e+03\ngamma_right: 1.3999999999999999e+00\na_right: 1.1832159566199232e+00\nRS p_1_tilde  = 3.7200525124077177e-01\nRS p_2_tilde  = 3.7200525124077177e-01\nSS p_1_tilde  = 3.7200525124077177e-01\nSS p_2_tilde  = 2.9291270455561289e+00\n   p^*_tilde  = 3.7200525124077177e-01\n   phi(p_*_t) = 1.4047089424401182e+00\n-> lambda_max = 6.6963146691962301e+00\n6.6963146691962301e+00\n\n1.0000000000000000e+00 2.1800000000000002e+00 1.0000000000000000e+02 1.3999999999999999e+00\n1.0000000000000000e+00 2.1800000000000002e+00 1.0000000000000000e-02 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 2.1800000000000002e+00\np_left: 1.0000000000000000e+02\ngamma_left: 1.3999999999999999e+00\na_left: 1.1832159566199232e+01\nrho_right: 1.0000000000000000e+00\nu_right: 2.1800000000000002e+00\np_right: 1.0000000000000000e-02\ngamma_right: 1.3999999999999999e+00\na_right: 1.1832159566199231e-01\nRS p_1_tilde  = 8.2983069275580775e+01\nRS p_2_tilde  = 8.2983069275580775e+01\nSS p_1_tilde  = 8.2983069275580817e+01\nSS p_2_tilde  = 4.8079470435532315e+01\n   p^*_tilde  = 8.2983069275580775e+01\n   phi(p_*_t) = 6.7590391906237013e+00\n-> lambda_max = 1.2159062237038954e+01\n1.2159062237038954e+01\n\n1.5000000000000000e+00 1.0000000000000000e+02 2.2000000000000000e+01 2.0041781532448066e+00\n7.0000000000000000e+00 0.0000000000000000e+00 1.2000000000000000e+01 5.7237635705670113e+00\nrho_left: 1.5000000000000000e+00\nu_left: 1.0000000000000000e+02\np_left: 2.2000000000000000e+01\ngamma_left: 2.0041781532448066e+00\na_left: 5.4339200034574526e+00\nrho_right: 7.0000000000000000e+00\nu_right: 0.0000000000000000e+00\np_right: 1.2000000000000000e+01\ngamma_right: 5.7237635705670113e+00\na_right: 3.1658555864814155e+00\nRS p_1_tilde  = 5.0574652545080899e+03\nRS p_2_tilde  = 1.7441473849212055e+05\nSS p_1_tilde  = 1.9156532462115449e+05\nSS p_2_tilde  = 1.7892338477385347e+04\n   p^*_tilde  = 1.7892338477385347e+04\n   phi(p_*_t) = 1.6040010273203080e+01\n-> lambda_max = 9.1819017351103540e+01\n9.1819017351103540e+01\n\n1.5000000000000000e+00 0.0000000000000000e+00 2.2000000000000000e+01 2.0041781532448066e+00\n7.0000000000000000e+00 0.0000000000000000e+00 1.2000000000000000e+01 5.7237635705670113e+00\nrho_left: 1.5000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 2.2000000000000000e+01\ngamma_left: 2.0041781532448066e+00\na_left: 5.4339200034574526e+00\nrho_right: 7.0000000000000000e+00\nu_right: 0.0000000000000000e+00\np_right: 1.2000000000000000e+01\ngamma_right: 5.7237635705670113e+00\na_right: 3.1658555864814155e+00\nRS p_1_tilde  = 2.0856026012445138e+01\nRS p_2_tilde  = 2.0904760959713094e+01\nSS p_1_tilde  = 2.0876188129159079e+01\nSS p_2_tilde  = 1.9688757504099055e+01\n   p^*_tilde  = 2.0856026012445138e+01\n   phi(p_*_t) = 1.8910277918067978e-01\n-> lambda_max = 5.4339200034574526e+00\n5.4339200034574526e+00\n\n3.5000000000000000e+03 2.0000000000000000e+01 2.3000000000000000e+10 1.1801508858712090e+02\n2.4000000000000000e+03 0.0000000000000000e+00 1.5000000000000000e+11 2.8761770391786854e+00\nrho_left: 3.5000000000000000e+03\nu_left: 2.0000000000000000e+01\np_left: 2.3000000000000000e+10\ngamma_left: 1.1801508858712090e+02\na_left: 2.7848298422005613e+04\nrho_right: 2.4000000000000000e+03\nu_right: 0.0000000000000000e+00\np_right: 1.5000000000000000e+11\ngamma_right: 2.8761770391786854e+00\na_right: 1.3407500324395589e+04\nRS p_1_tilde  = 1.4025323174232843e+11\nRS p_2_tilde  = 1.4201086622981799e+11\nSS p_1_tilde  = 1.4134908461823001e+11\nSS p_2_tilde  = 1.3209042937604797e+11\n   p^*_tilde  = 1.4025323174232843e+11\n   phi(p_*_t) = 3.0684790396394271e+02\n-> lambda_max = 5.2602099971187599e+04\n5.2602099971187599e+04\n\n3.5000000000000000e+03 2.0000000000000000e+01 2.3000000000000000e+10 1.1801508858712090e+02\n3.3000000000000000e+03 0.0000000000000000e+00 2.2000000000000000e+10 8.2392709087064375e+00\nrho_left: 3.5000000000000000e+03\nu_left: 2.0000000000000000e+01\np_left: 2.3000000000000000e+10\ngamma_left: 1.1801508858712090e+02\na_left: 2.7848298422005613e+04\nrho_right: 3.3000000000000000e+03\nu_right: 0.0000000000000000e+00\np_right: 2.2000000000000000e+10\ngamma_right: 8.2392709087064375e+00\na_right: 7.4113745502915708e+03\nRS p_1_tilde  = 2.2797243514322433e+10\nRS p_2_tilde  = 2.2803021562292942e+10\nSS p_1_tilde  = 2.2753789255118675e+10\nSS p_2_tilde  = 2.2594095966063232e+10\n   p^*_tilde  = 2.2797243514322433e+10\n   phi(p_*_t) = 1.0185995765412130e+01\n-> lambda_max = 2.7828298422005613e+04\n2.7828298422005613e+04\n\n3.5000000000000000e+03 2.0000000000000000e+01 2.3000000000000000e+10 1.1801508858712090e+02\n3.0000000000000000e+00 0.0000000000000000e+00 2.2000000000000000e+06 1.0453481734270629e+00\nrho_left: 3.5000000000000000e+03\nu_left: 2.0000000000000000e+01\np_left: 2.3000000000000000e+10\ngamma_left: 1.1801508858712090e+02\na_left: 2.7848298422005613e+04\nrho_right: 3.0000000000000000e+00\nu_right: 0.0000000000000000e+00\np_right: 2.2000000000000000e+06\ngamma_right: 1.0453481734270629e+00\na_right: 8.7555048998511757e+02\nRS p_1_tilde  = 1.5426478579391041e+10\nRS p_2_tilde  = 3.9395678751114751e+06\nSS p_1_tilde  = 2.4220969794201753e+06\nSS p_2_tilde  = 6.4989009638792388e+07\n   p^*_tilde  = 3.9395678751114751e+06\n   phi(p_*_t) = 7.7815361027602421e+00\n-> lambda_max = 2.7828298422005613e+04\n2.7828298422005613e+04\n\n3.5000000000000000e+02 2.0000000000000000e+01 2.3000000000000000e+05 1.0000474957444776e+00\n3.0000000000000000e+00 0.0000000000000000e+00 2.2000000000000000e+06 1.0453481734270629e+00\nrho_left: 3.5000000000000000e+02\nu_left: 2.0000000000000000e+01\np_left: 2.3000000000000000e+05\ngamma_left: 1.0000474957444776e+00\na_left: 2.5635406543140395e+01\nrho_right: 3.0000000000000000e+00\nu_right: 0.0000000000000000e+00\np_right: 2.2000000000000000e+06\ngamma_right: 1.0453481734270629e+00\na_right: 8.7555048998511757e+02\nRS p_1_tilde  = 2.1965659271167400e+06\nRS p_2_tilde  = 8.0321666787454474e+35\nSS p_1_tilde  = 5.2811885339027445e+05\nSS p_2_tilde  = 2.0762723730526464e+06\n   p^*_tilde  = 2.1965659271167400e+06\n   phi(p_*_t) = 4.9616160479524822e+01\n-> lambda_max = 8.7555048998511757e+02\n8.7555048998511757e+02\n\n1.5000000000000000e+01 2.0000000000000000e+01 7.3000000000000000e+08 2.2145329586703819e+00\n5.0000000000000000e+02 0.0000000000000000e+00 2.2000000000000000e+09 1.2899388697970200e+00\nrho_left: 1.5000000000000000e+01\nu_left: 2.0000000000000000e+01\np_left: 7.3000000000000000e+08\ngamma_left: 2.2145329586703819e+00\na_left: 1.0381422702209875e+04\nrho_right: 5.0000000000000000e+02\nu_right: 0.0000000000000000e+00\np_right: 2.2000000000000000e+09\ngamma_right: 1.2899388697970200e+00\na_right: 2.3823792786008880e+03\nRS p_1_tilde  = 1.2200916685414193e+09\nRS p_2_tilde  = 1.2543388114413664e+09\nSS p_1_tilde  = 1.2543388114413664e+09\nSS p_2_tilde  = 9.8289530031654692e+08\n   p^*_tilde  = 1.2200916685414193e+09\n   phi(p_*_t) = 1.5071738424442851e+03\n-> lambda_max = 1.2640481744225293e+04\n1.2640481744225293e+04\n\n1.0000000000000000e+00 0.0000000000000000e+00 6.6666666666666666e-02 2.9900000000000002e+00\n1.0000000000000000e-03 0.0000000000000000e+00 6.6666666666666669e-11 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 6.6666666666666666e-02\ngamma_left: 2.9900000000000002e+00\na_left: 4.4646761734008583e-01\nrho_right: 1.0000000000000000e-03\nu_right: 0.0000000000000000e+00\np_right: 6.6666666666666669e-11\ngamma_right: 1.3999999999999999e+00\na_right: 3.0550504633038936e-04\nRS p_1_tilde  = 6.6562450519838737e-02\nRS p_2_tilde  = 1.6858067487616077e-01\nSS p_1_tilde  = 6.6400989005996877e-02\nSS p_2_tilde  = 1.4003512708569408e-03\n   p^*_tilde  = 6.6562450519838737e-02\n   phi(p_*_t) = 2.3834816624660711e+00\n-> lambda_max = 4.4646761734008583e-01\n4.4646761734008583e-01\n\n1.0000000000000000e+00 0.0000000000000000e+00 6.6666666666666666e-02 1.0100000000000000e+00\n1.0000000000000000e-03 0.0000000000000000e+00 6.6666666666666669e-11 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 6.6666666666666666e-02\ngamma_left: 1.0100000000000000e+00\na_left: 2.5948667274704751e-01\nrho_right: 1.0000000000000000e-03\nu_right: 0.0000000000000000e+00\np_right: 6.6666666666666669e-11\ngamma_right: 1.3999999999999999e+00\na_right: 3.0550504633038936e-04\nRS p_1_tilde  = 6.6664560356925001e-02\nRS p_2_tilde  = 6.6664578484248158e-02\nSS p_1_tilde  = 6.6664578484258719e-02\nSS p_2_tilde  = 2.3734914305565576e-03\n   p^*_tilde  = 6.6664560356925001e-02\n   phi(p_*_t) = 2.3869770722449699e+00\n-> lambda_max = 2.5948667274704751e-01\n2.5948667274704751e-01\n\n1.0000000000000000e+00 0.0000000000000000e+00 6.6666666666666666e-02 2.9600000000000000e+00\n1.0000000000000000e-03 0.0000000000000000e+00 6.6666666666666669e-11 2.9900000000000002e+00\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 6.6666666666666666e-02\ngamma_left: 2.9600000000000000e+00\na_left: 4.4422216663887154e-01\nrho_right: 1.0000000000000000e-03\nu_right: 0.0000000000000000e+00\np_right: 6.6666666666666669e-11\ngamma_right: 2.9900000000000002e+00\na_right: 4.4646761734008588e-04\nRS p_1_tilde  = 6.6601252202132394e-02\nRS p_2_tilde  = 6.6601259181093372e-02\nSS p_1_tilde  = 6.6595167200530617e-02\nSS p_2_tilde  = 2.0142001493807381e-03\n   p^*_tilde  = 6.6601252202132394e-02\n   phi(p_*_t) = 1.6506995891545222e+00\n-> lambda_max = 4.4422216663887154e-01\n4.4422216663887154e-01\n\n1.0000000000000000e+00 0.0000000000000000e+00 6.6666666666666666e-02 4.0000000000000000e+01\n1.0000000000000000e-03 0.0000000000000000e+00 6.6666666666666669e-11 1.0009999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 6.6666666666666666e-02\ngamma_left: 4.0000000000000000e+01\na_left: 1.6329931618554521e+00\nrho_right: 1.0000000000000000e-03\nu_right: 0.0000000000000000e+00\np_right: 6.6666666666666669e-11\ngamma_right: 1.0009999999999999e+00\na_right: 2.5832795693330086e-04\nRS p_1_tilde  = 6.6604134324282999e-02\nRS p_2_tilde  = 8.0752050860224050e+06\nSS p_1_tilde  = 6.4765129957247182e-03\nSS p_2_tilde  = 3.3183029379127286e-04\n   p^*_tilde  = 6.6604134324282999e-02\n   phi(p_*_t) = 2.7967672802424457e+00\n-> lambda_max = 1.6329931618554521e+00\n1.6329931618554521e+00\n"
  },
  {
    "path": "tests/euler_aeos/riemann_solver-strict.cc",
    "content": "// force distinct symbols in test\n#define EulerAEOS EulerAEOSTest\n\n#include <hyperbolic_system.h>\n#include <multicomponent_vector.h>\n#define DEBUG_RIEMANN_SOLVER\n#include <riemann_solver.h>\n#include <riemann_solver.template.h>\n#include <simd.h>\n\nusing namespace ryujin::EulerAEOS;\nusing namespace ryujin;\nusing namespace dealii;\n\nint main()\n{\n  constexpr int dim = 1;\n\n  HyperbolicSystem hyperbolic_system;\n  RiemannSolver<dim, double>::Parameters riemann_solver_parameters;\n\n  static constexpr unsigned int n_precomputed_values =\n      HyperbolicSystemView<dim, double>::n_precomputed_values;\n  using precomputed_type =\n      Vectors::MultiComponentVector<double, n_precomputed_values>;\n  precomputed_type dummy;\n\n  RiemannSolver<dim> riemann_solver(\n      hyperbolic_system, riemann_solver_parameters, dummy);\n\n  std::stringstream parameters;\n  parameters << \"subsection HyperbolicSystem\\n\"\n             << \"set compute strict bounds = true\\n\"\n             << \"end\" << std::endl;\n  ParameterAcceptor::initialize(parameters);\n\n  const auto riemann_data = [&](const auto &state) {\n    const double rho = state[0];\n    const double u = state[1];\n    const double p = state[2];\n    const double gamma = state[3];\n\n    std::array<double, 5> result;\n    result[0] = rho;\n    result[1] = u;\n    result[2] = p;\n    result[3] = gamma;\n    const double interpolation_b =\n        hyperbolic_system.view<dim, double>().eos_interpolation_b();\n    const double x = 1. - interpolation_b * rho;\n    result[4] = std::sqrt(gamma * p / (rho * x));\n    return result;\n  };\n\n  const auto test = [&](const std::array<double, 4> &U_i,\n                        const std::array<double, 4> &U_j) {\n    std::cout << std::endl;\n    std::cout << U_i[0] << \" \" << U_i[1] << \" \" << U_i[2] << \" \" << U_i[3]\n              << std::endl;\n    std::cout << U_j[0] << \" \" << U_j[1] << \" \" << U_j[2] << \" \" << U_j[3]\n              << std::endl;\n    const auto rd_i = riemann_data(U_i);\n    const auto rd_j = riemann_data(U_j);\n    const auto lambda_max = riemann_solver.compute(rd_i, rd_j);\n    std::cout << lambda_max << std::endl;\n  };\n\n  const auto set_covolume = [&](const double covolume) {\n    /*\n     * Set the interpolatory covolume by selecting an equation of state\n     * with a covolume.\n     */\n    std::stringstream parameters;\n    parameters << \"subsection HyperbolicSystem\\n\"\n               << \"set equation of state = van der waals\\n\"\n               << \"subsection van der waals\\n\"\n               << \"set covolume b = \" << std::to_string(covolume) << \"\\n\"\n               << \"end\\n\"\n               << \"end\\n\"\n               << std::endl;\n    ParameterAcceptor::initialize(parameters);\n  };\n\n  std::cout << std::setprecision(16);\n  std::cout << std::scientific;\n\n  /*\n   * Test vectors for ideal gas with gamma = 1.4:\n   */\n\n  /* Leblanc:*/\n  test({1., 0., 2. / 30., 7. / 5.}, {1.e-3, 0., 2. / 3. * 1.e-10, 7. / 5.});\n  /* Sod:*/\n  test({1., 0., 1., 7. / 5.}, {0.125, 0., 0.1, 7. / 5.});\n  /* Lax:*/\n  test({0.445, 0.698, 3.528, 7. / 5.}, {0.5, 0., 0.571, 7. / 5.});\n  /* Fast shock case 1 (paper, section 5.2): */\n  test({1., 1.e1, 1.e3, 7. / 5.}, {1., 10., 0.01, 7. / 5.});\n  /* Fast shock case 2 (paper, section 5.2): */\n  test({5.99924, 19.5975, 460.894, 7. / 5.},\n       {5.99242, -6.19633, 46.0950, 7. / 5.});\n  /* Fast expansion and slow shock, case 1 (Paper, section 5.1) */\n  test({1., 0., 0.01, 7. / 5.}, {1., 0., 1.e2, 7. / 5.});\n  /* Fast expansion and slow shock, case 2 (Paper, section 5.1) */\n  test({1., -1., 0.01, 7. / 5.}, {1., -1., 1.e2, 7. / 5.});\n  /* Fast expansion and slow shock, case 3 (Paper, section 5.1) */\n  test({1., -2.18, 0.01, 7. / 5.}, {1., -2.18, 100., 7. / 5.});\n  /* Case 9:*/\n  test({1.0e-2, 0., 1.0e-2, 7. / 5.}, {1.e3, 0., 1.e3, 7. / 5.});\n  /* Case 10:*/\n  test({1.0, 2.18, 1.e2, 7. / 5.}, {1.0, 2.18, 0.01, 7. / 5.});\n\n  /*\n   * States with non-constant gamma values:\n   */\n\n  /* Shock-shock */\n  set_covolume(0.003);\n  test({1.5, 100., 22., 2.0041781532448066}, {7., 0., 12., 5.7237635705670113});\n\n  /* Shock-expansion */\n  set_covolume(0.003);\n  test({1.5, 0., 22., 2.0041781532448066}, {7., 0., 12., 5.7237635705670113});\n\n  /* Mie-Gruneisen: shock-expansion 1, gamma_min != gamma_m */\n  set_covolume(0.);\n  test({3500., 20., 2.3e10, 118.01508858712090},\n       {2400., 0., 1.5e11, 2.8761770391786854});\n\n  /* Mie-Gruneisen: shock-expansion 2, gamma_min = gamma_m */\n  set_covolume(0.);\n  test({3500., 20., 2.3e10, 118.01508858712090},\n       {3300., 0., 2.2e10, 8.2392709087064375});\n\n  /* Mie-Gruneisen: shock-expansion 3, gamma_min = gamma_m */\n  set_covolume(0.);\n  test({3500., 20., 2.3e10, 118.01508858712090},\n       {3., 0., 2.2e6, 1.0453481734270629});\n\n  /* Mie-Gruneisen: shock-expansion 4, gamma_min = gamma_m */\n  set_covolume(0.);\n  test({350., 20., 2.3e5, 1.0000474957444776},\n       {3., 0., 2.2e6, 1.0453481734270629});\n\n  /* Mie-Gruneisen: shock-expansion 5, gamma_min != gamma_m */\n  set_covolume(0.);\n  test({15., 20., 7.3e8, 2.2145329586703819},\n       {500., 0., 2.2e9, 1.2899388697970200});\n\n  /*\n   * States with crazy two-rarefaction pressure:\n   */\n\n  test({1., 300., 1, 1.4}, {0.125, -300., 0.1, 1.4});\n\n  /*\n   * States with crazy gamma values:\n   */\n\n  test({1., 0., 2. / 30., 2.99}, {1.e-3, 0., 2. / 3. * 1.e-10, 1.40});\n\n  test({1., 0., 2. / 30., 1.01}, {1.e-3, 0., 2. / 3. * 1.e-10, 1.40});\n\n  test({1., 0., 2. / 30., 2.96}, {1.e-3, 0., 2. / 3. * 1.e-10, 2.99});\n\n  test({1., 0., 2. / 30., 40.0}, {1.e-3, 0., 2. / 3. * 1.e-10, 1.001});\n\n  return 0;\n}\n"
  },
  {
    "path": "tests/euler_aeos/riemann_solver-strict.threads=2.output",
    "content": "\n1.0000000000000000e+00 0.0000000000000000e+00 6.6666666666666666e-02 1.3999999999999999e+00\n1.0000000000000000e-03 0.0000000000000000e+00 6.6666666666666669e-11 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 6.6666666666666666e-02\ngamma_left: 1.3999999999999999e+00\na_left: 3.0550504633038933e-01\nrho_right: 1.0000000000000000e-03\nu_right: 0.0000000000000000e+00\np_right: 6.6666666666666669e-11\ngamma_right: 1.3999999999999999e+00\na_right: 3.0550504633038936e-04\nRS p_1_tilde  = 5.8723550288294901e-02\nRS p_2_tilde  = 5.8723550288294901e-02\nSS p_1_tilde  = 5.8723550288294839e-02\nSS p_2_tilde  = 1.8962827388271815e-03\n   p^*_tilde  = 5.8723550288294901e-02\n   phi(p_*_t) = 6.9680132444205416e+00\n-> lambda_max = 8.3945375309952137e+00\n8.3945375309952137e+00\n\n1.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e+00 1.3999999999999999e+00\n1.2500000000000000e-01 0.0000000000000000e+00 1.0000000000000001e-01 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 1.0000000000000000e+00\ngamma_left: 1.3999999999999999e+00\na_left: 1.1832159566199232e+00\nrho_right: 1.2500000000000000e-01\nu_right: 0.0000000000000000e+00\np_right: 1.0000000000000001e-01\ngamma_right: 1.3999999999999999e+00\na_right: 1.0583005244258361e+00\nRS p_1_tilde  = 3.0676664667059678e-01\nRS p_2_tilde  = 3.0676664667059678e-01\nSS p_1_tilde  = 3.0676664667059667e-01\nSS p_2_tilde  = 3.2333029409004022e-01\n   p^*_tilde  = 3.0676664667059678e-01\n   phi(p_*_t) = 1.9786963438474081e-02\n-> lambda_max = 1.7620896140769142e+00\n1.7620896140769142e+00\n\n4.4500000000000001e-01 6.9799999999999995e-01 3.5280000000000000e+00 1.3999999999999999e+00\n5.0000000000000000e-01 0.0000000000000000e+00 5.7099999999999995e-01 1.3999999999999999e+00\nrho_left: 4.4500000000000001e-01\nu_left: 6.9799999999999995e-01\np_left: 3.5280000000000000e+00\ngamma_left: 1.3999999999999999e+00\na_left: 3.3315650740600322e+00\nrho_right: 5.0000000000000000e-01\nu_right: 0.0000000000000000e+00\np_right: 5.7099999999999995e-01\ngamma_right: 1.3999999999999999e+00\na_right: 1.2644366334458994e+00\nRS p_1_tilde  = 2.5096631320093397e+00\nRS p_2_tilde  = 2.5096631320093397e+00\nSS p_1_tilde  = 2.5096631320093410e+00\nSS p_2_tilde  = 2.4778604535394879e+00\n   p^*_tilde  = 2.5096631320093397e+00\n   phi(p_*_t) = 6.1653197162831685e-02\n-> lambda_max = 2.6335650740600323e+00\n2.6335650740600323e+00\n\n1.0000000000000000e+00 1.0000000000000000e+01 1.0000000000000000e+03 1.3999999999999999e+00\n1.0000000000000000e+00 1.0000000000000000e+01 1.0000000000000000e-02 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 1.0000000000000000e+01\np_left: 1.0000000000000000e+03\ngamma_left: 1.3999999999999999e+00\na_left: 3.7416573867739416e+01\nrho_right: 1.0000000000000000e+00\nu_right: 1.0000000000000000e+01\np_right: 1.0000000000000000e-02\ngamma_right: 1.3999999999999999e+00\na_right: 1.1832159566199231e-01\nRS p_1_tilde  = 9.1244932710571970e+02\nRS p_2_tilde  = 9.1244932710571970e+02\nSS p_1_tilde  = 9.1244932710571993e+02\nSS p_2_tilde  = 4.8074609902285869e+02\n   p^*_tilde  = 9.1244932710571970e+02\n   phi(p_*_t) = 2.5141795977374755e+01\n-> lambda_max = 4.3089895625807941e+01\n4.3089895625807941e+01\n\n5.9992400000000004e+00 1.9597500000000000e+01 4.6089400000000001e+02 1.3999999999999999e+00\n5.9924200000000001e+00 -6.1963299999999997e+00 4.6094999999999999e+01 1.3999999999999999e+00\nrho_left: 5.9992400000000004e+00\nu_left: 1.9597500000000000e+01\np_left: 4.6089400000000001e+02\ngamma_left: 1.3999999999999999e+00\na_left: 1.0370899528836672e+01\nrho_right: 5.9924200000000001e+00\nu_right: -6.1963299999999997e+00\np_right: 4.6094999999999999e+01\ngamma_right: 1.3999999999999999e+00\na_right: 3.2816314493370298e+00\nRS p_1_tilde  = 2.3226554570159969e+03\nRS p_2_tilde  = 2.3226554570159969e+03\nSS p_1_tilde  = 2.3226554570159974e+03\nSS p_2_tilde  = 1.7599311105107934e+03\n   p^*_tilde  = 1.7599311105107934e+03\n   phi(p_*_t) = 7.0445120003856943e-01\n-> lambda_max = 1.2617757915202827e+01\n1.2617757915202827e+01\n\n1.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e-02 1.3999999999999999e+00\n1.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e+02 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 1.0000000000000000e-02\ngamma_left: 1.3999999999999999e+00\na_left: 1.1832159566199231e-01\nrho_right: 1.0000000000000000e+00\nu_right: 0.0000000000000000e+00\np_right: 1.0000000000000000e+02\ngamma_right: 1.3999999999999999e+00\na_right: 1.1832159566199232e+01\nRS p_1_tilde  = 8.2983069275580775e+01\nRS p_2_tilde  = 8.2983069275580775e+01\nSS p_1_tilde  = 8.2983069275580775e+01\nSS p_2_tilde  = 4.8079470435532315e+01\n   p^*_tilde  = 8.2983069275580775e+01\n   phi(p_*_t) = 6.7590391906237013e+00\n-> lambda_max = 1.1832159566199232e+01\n1.1832159566199232e+01\n\n1.0000000000000000e+00 -1.0000000000000000e+00 1.0000000000000000e-02 1.3999999999999999e+00\n1.0000000000000000e+00 -1.0000000000000000e+00 1.0000000000000000e+02 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: -1.0000000000000000e+00\np_left: 1.0000000000000000e-02\ngamma_left: 1.3999999999999999e+00\na_left: 1.1832159566199231e-01\nrho_right: 1.0000000000000000e+00\nu_right: -1.0000000000000000e+00\np_right: 1.0000000000000000e+02\ngamma_right: 1.3999999999999999e+00\na_right: 1.1832159566199232e+01\nRS p_1_tilde  = 8.2983069275580775e+01\nRS p_2_tilde  = 8.2983069275580775e+01\nSS p_1_tilde  = 8.2983069275580775e+01\nSS p_2_tilde  = 4.8079470435532315e+01\n   p^*_tilde  = 8.2983069275580775e+01\n   phi(p_*_t) = 6.7590391906237013e+00\n-> lambda_max = 1.0979062237038955e+01\n1.0979062237038955e+01\n\n1.0000000000000000e+00 -2.1800000000000002e+00 1.0000000000000000e-02 1.3999999999999999e+00\n1.0000000000000000e+00 -2.1800000000000002e+00 1.0000000000000000e+02 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: -2.1800000000000002e+00\np_left: 1.0000000000000000e-02\ngamma_left: 1.3999999999999999e+00\na_left: 1.1832159566199231e-01\nrho_right: 1.0000000000000000e+00\nu_right: -2.1800000000000002e+00\np_right: 1.0000000000000000e+02\ngamma_right: 1.3999999999999999e+00\na_right: 1.1832159566199232e+01\nRS p_1_tilde  = 8.2983069275580775e+01\nRS p_2_tilde  = 8.2983069275580775e+01\nSS p_1_tilde  = 8.2983069275580775e+01\nSS p_2_tilde  = 4.8079470435532315e+01\n   p^*_tilde  = 8.2983069275580775e+01\n   phi(p_*_t) = 6.7590391906237013e+00\n-> lambda_max = 1.2159062237038954e+01\n1.2159062237038954e+01\n\n1.0000000000000000e-02 0.0000000000000000e+00 1.0000000000000000e-02 1.3999999999999999e+00\n1.0000000000000000e+03 0.0000000000000000e+00 1.0000000000000000e+03 1.3999999999999999e+00\nrho_left: 1.0000000000000000e-02\nu_left: 0.0000000000000000e+00\np_left: 1.0000000000000000e-02\ngamma_left: 1.3999999999999999e+00\na_left: 1.1832159566199232e+00\nrho_right: 1.0000000000000000e+03\nu_right: 0.0000000000000000e+00\np_right: 1.0000000000000000e+03\ngamma_right: 1.3999999999999999e+00\na_right: 1.1832159566199232e+00\nRS p_1_tilde  = 3.7200525124077177e-01\nRS p_2_tilde  = 3.7200525124077177e-01\nSS p_1_tilde  = 3.7200525124077177e-01\nSS p_2_tilde  = 2.9291270455561289e+00\n   p^*_tilde  = 3.7200525124077177e-01\n   phi(p_*_t) = 1.4047089424401182e+00\n-> lambda_max = 6.6963146691962301e+00\n6.6963146691962301e+00\n\n1.0000000000000000e+00 2.1800000000000002e+00 1.0000000000000000e+02 1.3999999999999999e+00\n1.0000000000000000e+00 2.1800000000000002e+00 1.0000000000000000e-02 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 2.1800000000000002e+00\np_left: 1.0000000000000000e+02\ngamma_left: 1.3999999999999999e+00\na_left: 1.1832159566199232e+01\nrho_right: 1.0000000000000000e+00\nu_right: 2.1800000000000002e+00\np_right: 1.0000000000000000e-02\ngamma_right: 1.3999999999999999e+00\na_right: 1.1832159566199231e-01\nRS p_1_tilde  = 8.2983069275580775e+01\nRS p_2_tilde  = 8.2983069275580775e+01\nSS p_1_tilde  = 8.2983069275580817e+01\nSS p_2_tilde  = 4.8079470435532315e+01\n   p^*_tilde  = 8.2983069275580775e+01\n   phi(p_*_t) = 6.7590391906237013e+00\n-> lambda_max = 1.2159062237038954e+01\n1.2159062237038954e+01\n\n1.5000000000000000e+00 1.0000000000000000e+02 2.2000000000000000e+01 2.0041781532448066e+00\n7.0000000000000000e+00 0.0000000000000000e+00 1.2000000000000000e+01 5.7237635705670113e+00\nrho_left: 1.5000000000000000e+00\nu_left: 1.0000000000000000e+02\np_left: 2.2000000000000000e+01\ngamma_left: 2.0041781532448066e+00\na_left: 5.4339200034574526e+00\nrho_right: 7.0000000000000000e+00\nu_right: 0.0000000000000000e+00\np_right: 1.2000000000000000e+01\ngamma_right: 5.7237635705670113e+00\na_right: 3.1658555864814155e+00\nRS p_1_tilde  = 4.9365679206103723e+03\nRS p_2_tilde  = 1.7025505863088969e+05\nSS p_1_tilde  = 1.8698827064740786e+05\nSS p_2_tilde  = 1.7870707469526966e+04\n   p^*_tilde  = 1.7870707469526966e+04\n   phi(p_*_t) = 1.5971962570955895e+01\n-> lambda_max = 9.3653527931081371e+01\n9.3653527931081371e+01\n\n1.5000000000000000e+00 0.0000000000000000e+00 2.2000000000000000e+01 2.0041781532448066e+00\n7.0000000000000000e+00 0.0000000000000000e+00 1.2000000000000000e+01 5.7237635705670113e+00\nrho_left: 1.5000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 2.2000000000000000e+01\ngamma_left: 2.0041781532448066e+00\na_left: 5.4339200034574526e+00\nrho_right: 7.0000000000000000e+00\nu_right: 0.0000000000000000e+00\np_right: 1.2000000000000000e+01\ngamma_right: 5.7237635705670113e+00\na_right: 3.1658555864814155e+00\nRS p_1_tilde  = 2.0843455346157853e+01\nRS p_2_tilde  = 2.0894211215041093e+01\nSS p_1_tilde  = 2.0865394227858054e+01\nSS p_2_tilde  = 1.9684207500367314e+01\n   p^*_tilde  = 2.0843455346157853e+01\n   phi(p_*_t) = 1.8859790097336063e-01\n-> lambda_max = 5.4339200034574526e+00\n5.4339200034574526e+00\n\n3.5000000000000000e+03 2.0000000000000000e+01 2.3000000000000000e+10 1.1801508858712090e+02\n2.4000000000000000e+03 0.0000000000000000e+00 1.5000000000000000e+11 2.8761770391786854e+00\nrho_left: 3.5000000000000000e+03\nu_left: 2.0000000000000000e+01\np_left: 2.3000000000000000e+10\ngamma_left: 1.1801508858712090e+02\na_left: 2.7848298422005613e+04\nrho_right: 2.4000000000000000e+03\nu_right: 0.0000000000000000e+00\np_right: 1.5000000000000000e+11\ngamma_right: 2.8761770391786854e+00\na_right: 1.3407500324395589e+04\nRS p_1_tilde  = 1.4025323174221558e+11\nRS p_2_tilde  = 1.4201086622973593e+11\nSS p_1_tilde  = 1.4134908461814151e+11\nSS p_2_tilde  = 1.3209042937602786e+11\n   p^*_tilde  = 1.4025323174221558e+11\n   phi(p_*_t) = 3.0684790396076323e+02\n-> lambda_max = 5.2602099971581149e+04\n5.2602099971581149e+04\n\n3.5000000000000000e+03 2.0000000000000000e+01 2.3000000000000000e+10 1.1801508858712090e+02\n3.3000000000000000e+03 0.0000000000000000e+00 2.2000000000000000e+10 8.2392709087064375e+00\nrho_left: 3.5000000000000000e+03\nu_left: 2.0000000000000000e+01\np_left: 2.3000000000000000e+10\ngamma_left: 1.1801508858712090e+02\na_left: 2.7848298422005613e+04\nrho_right: 3.3000000000000000e+03\nu_right: 0.0000000000000000e+00\np_right: 2.2000000000000000e+10\ngamma_right: 8.2392709087064375e+00\na_right: 7.4113745502915708e+03\nRS p_1_tilde  = 2.2797243514311752e+10\nRS p_2_tilde  = 2.2803021562280891e+10\nSS p_1_tilde  = 2.2753789255105709e+10\nSS p_2_tilde  = 2.2594095966058762e+10\n   p^*_tilde  = 2.2797243514311752e+10\n   phi(p_*_t) = 1.0185995765187972e+01\n-> lambda_max = 2.7828298422005613e+04\n2.7828298422005613e+04\n\n3.5000000000000000e+03 2.0000000000000000e+01 2.3000000000000000e+10 1.1801508858712090e+02\n3.0000000000000000e+00 0.0000000000000000e+00 2.2000000000000000e+06 1.0453481734270629e+00\nrho_left: 3.5000000000000000e+03\nu_left: 2.0000000000000000e+01\np_left: 2.3000000000000000e+10\ngamma_left: 1.1801508858712090e+02\na_left: 2.7848298422005613e+04\nrho_right: 3.0000000000000000e+00\nu_right: 0.0000000000000000e+00\np_right: 2.2000000000000000e+06\ngamma_right: 1.0453481734270629e+00\na_right: 8.7555048998511757e+02\nRS p_1_tilde  = 1.5426478427707598e+10\nRS p_2_tilde  = 3.9395674823204307e+06\nSS p_1_tilde  = 2.4220969328407627e+06\nSS p_2_tilde  = 6.4989009637691483e+07\n   p^*_tilde  = 3.9395674823204307e+06\n   phi(p_*_t) = 7.7814794391570103e+00\n-> lambda_max = 2.7828298422005613e+04\n2.7828298422005613e+04\n\n3.5000000000000000e+02 2.0000000000000000e+01 2.3000000000000000e+05 1.0000474957444776e+00\n3.0000000000000000e+00 0.0000000000000000e+00 2.2000000000000000e+06 1.0453481734270629e+00\nrho_left: 3.5000000000000000e+02\nu_left: 2.0000000000000000e+01\np_left: 2.3000000000000000e+05\ngamma_left: 1.0000474957444776e+00\na_left: 2.5635406543140395e+01\nrho_right: 3.0000000000000000e+00\nu_right: 0.0000000000000000e+00\np_right: 2.2000000000000000e+06\ngamma_right: 1.0453481734270629e+00\na_right: 8.7555048998511757e+02\nRS p_1_tilde  = 2.1965659233776061e+06\nRS p_2_tilde  = 8.0326196662183801e+35\nSS p_1_tilde  = 5.2811824081277614e+05\nSS p_2_tilde  = 2.0762723673749219e+06\n   p^*_tilde  = 2.1965659233776061e+06\n   phi(p_*_t) = 4.9616166754114815e+01\n-> lambda_max = 8.7555048998511757e+02\n8.7555048998511757e+02\n\n1.5000000000000000e+01 2.0000000000000000e+01 7.3000000000000000e+08 2.2145329586703819e+00\n5.0000000000000000e+02 0.0000000000000000e+00 2.2000000000000000e+09 1.2899388697970200e+00\nrho_left: 1.5000000000000000e+01\nu_left: 2.0000000000000000e+01\np_left: 7.3000000000000000e+08\ngamma_left: 2.2145329586703819e+00\na_left: 1.0381422702209875e+04\nrho_right: 5.0000000000000000e+02\nu_right: 0.0000000000000000e+00\np_right: 2.2000000000000000e+09\ngamma_right: 1.2899388697970200e+00\na_right: 2.3823792786008880e+03\nRS p_1_tilde  = 1.2200916684437129e+09\nRS p_2_tilde  = 1.2543388113522141e+09\nSS p_1_tilde  = 1.2543388113522141e+09\nSS p_2_tilde  = 9.8289530031049967e+08\n   p^*_tilde  = 1.2200916684437129e+09\n   phi(p_*_t) = 1.5071738421543907e+03\n-> lambda_max = 1.2640481745232328e+04\n1.2640481745232328e+04\n\n1.0000000000000000e+00 3.0000000000000000e+02 1.0000000000000000e+00 1.3999999999999999e+00\n1.2500000000000000e-01 -3.0000000000000000e+02 1.0000000000000001e-01 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 3.0000000000000000e+02\np_left: 1.0000000000000000e+00\ngamma_left: 1.3999999999999999e+00\na_left: 1.1832159566199232e+00\nrho_right: 1.2500000000000000e-01\nu_right: -3.0000000000000000e+02\np_right: 1.0000000000000001e-01\ngamma_right: 1.3999999999999999e+00\na_right: 1.0583005244258361e+00\nRS p_1_tilde  = 4.4009424225516168e+11\nRS p_2_tilde  = 4.4009424225516168e+11\nSS p_1_tilde  = 4.4009424225516156e+11\nSS p_2_tilde  = 3.1034863706855802e+04\n   p^*_tilde  = 3.1034863706855802e+04\n   phi(p_*_t) = 1.5672283636362181e+01\n-> lambda_max = 2.4583408796612875e+02\n2.4583408796612875e+02\n\n1.0000000000000000e+00 0.0000000000000000e+00 6.6666666666666666e-02 2.9900000000000002e+00\n1.0000000000000000e-03 0.0000000000000000e+00 6.6666666666666669e-11 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 6.6666666666666666e-02\ngamma_left: 2.9900000000000002e+00\na_left: 4.4646761734008583e-01\nrho_right: 1.0000000000000000e-03\nu_right: 0.0000000000000000e+00\np_right: 6.6666666666666669e-11\ngamma_right: 1.3999999999999999e+00\na_right: 3.0550504633038936e-04\nRS p_1_tilde  = 5.5624820417800008e-02\nRS p_2_tilde  = 2.0851010080017713e+06\nSS p_1_tilde  = 4.2068839637523107e-02\nSS p_2_tilde  = 1.3093308231259685e-03\n   p^*_tilde  = 5.5624820417800008e-02\n   phi(p_*_t) = 6.7821386915218982e+00\n-> lambda_max = 8.1700541316868502e+00\n8.1700541316868502e+00\n\n1.0000000000000000e+00 0.0000000000000000e+00 6.6666666666666666e-02 1.0100000000000000e+00\n1.0000000000000000e-03 0.0000000000000000e+00 6.6666666666666669e-11 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 6.6666666666666666e-02\ngamma_left: 1.0100000000000000e+00\na_left: 2.5948667274704751e-01\nrho_right: 1.0000000000000000e-03\nu_right: 0.0000000000000000e+00\np_right: 6.6666666666666669e-11\ngamma_right: 1.3999999999999999e+00\na_right: 3.0550504633038936e-04\nRS p_1_tilde  = 6.6415756388980215e-02\nRS p_2_tilde  = 6.6623858660829760e-02\nSS p_1_tilde  = 6.6623858660830246e-02\nSS p_2_tilde  = 2.2213713553365356e-03\n   p^*_tilde  = 6.6415756388980215e-02\n   phi(p_*_t) = 7.4385516197631727e+00\n-> lambda_max = 8.9274244707031603e+00\n8.9274244707031603e+00\n\n1.0000000000000000e+00 0.0000000000000000e+00 6.6666666666666666e-02 2.9600000000000000e+00\n1.0000000000000000e-03 0.0000000000000000e+00 6.6666666666666669e-11 2.9900000000000002e+00\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 6.6666666666666666e-02\ngamma_left: 2.9600000000000000e+00\na_left: 4.4422216663887154e-01\nrho_right: 1.0000000000000000e-03\nu_right: 0.0000000000000000e+00\np_right: 6.6666666666666669e-11\ngamma_right: 2.9900000000000002e+00\na_right: 4.4646761734008588e-04\nRS p_1_tilde  = 9.8117204055763567e-03\nRS p_2_tilde  = 1.0210027643897120e-02\nSS p_1_tilde  = 8.9882488242153998e-03\nSS p_2_tilde  = 1.6869545981586475e-03\n   p^*_tilde  = 9.8117204055763567e-03\n   phi(p_*_t) = 2.0047626369527043e+00\n-> lambda_max = 4.4242945511638538e+00\n4.4242945511638538e+00\n\n1.0000000000000000e+00 0.0000000000000000e+00 6.6666666666666666e-02 4.0000000000000000e+01\n1.0000000000000000e-03 0.0000000000000000e+00 6.6666666666666669e-11 1.0009999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 6.6666666666666666e-02\ngamma_left: 4.0000000000000000e+01\na_left: 1.6329931618554521e+00\nrho_right: 1.0000000000000000e-03\nu_right: 0.0000000000000000e+00\np_right: 6.6666666666666669e-11\ngamma_right: 1.0009999999999999e+00\na_right: 2.5832795693330086e-04\nRS p_1_tilde  = 6.5458747897678402e-02\nRS p_2_tilde  = 2.6665134777986636e+120\nSS p_1_tilde  = 5.5599488097791917e-10\nSS p_2_tilde  = 3.3175752067092170e-04\n   p^*_tilde  = 6.5458747897678402e-02\n   phi(p_*_t) = 8.0878929864984261e+00\n-> lambda_max = 8.0926804750750279e+00\n8.0926804750750279e+00\n"
  },
  {
    "path": "tests/euler_aeos/riemann_solver.cc",
    "content": "// force distinct symbols in test\n#define EulerAEOS EulerAEOSTest\n\n#include <hyperbolic_system.h>\n#include <multicomponent_vector.h>\n#define DEBUG_RIEMANN_SOLVER\n#include <riemann_solver.h>\n#include <riemann_solver.template.h>\n#include <simd.h>\n\nusing namespace ryujin::EulerAEOS;\nusing namespace ryujin;\nusing namespace dealii;\n\nint main()\n{\n  constexpr int dim = 1;\n\n  HyperbolicSystem hyperbolic_system;\n  RiemannSolver<dim, double>::Parameters riemann_solver_parameters;\n\n  static constexpr unsigned int n_precomputed_values =\n      HyperbolicSystemView<dim, double>::n_precomputed_values;\n  using precomputed_type =\n      Vectors::MultiComponentVector<double, n_precomputed_values>;\n  precomputed_type dummy;\n\n  RiemannSolver<dim> riemann_solver(\n      hyperbolic_system, riemann_solver_parameters, dummy);\n\n  std::stringstream parameters;\n  parameters << \"subsection HyperbolicSystem\\n\"\n             << \"set compute strict bounds = false\\n\"\n             << \"end\" << std::endl;\n  ParameterAcceptor::initialize(parameters);\n\n  const auto riemann_data = [&](const auto &state) {\n    const double rho = state[0];\n    const double u = state[1];\n    const double p = state[2];\n    const double gamma = state[3];\n\n    std::array<double, 5> result;\n    result[0] = rho;\n    result[1] = u;\n    result[2] = p;\n    result[3] = gamma;\n    const double interpolation_b =\n        hyperbolic_system.view<dim, double>().eos_interpolation_b();\n    const double x = 1. - interpolation_b * rho;\n    result[4] = std::sqrt(gamma * p / (rho * x));\n    return result;\n  };\n\n  const auto test = [&](const std::array<double, 4> &U_i,\n                        const std::array<double, 4> &U_j) {\n    std::cout << std::endl;\n    std::cout << U_i[0] << \" \" << U_i[1] << \" \" << U_i[2] << \" \" << U_i[3]\n              << std::endl;\n    std::cout << U_j[0] << \" \" << U_j[1] << \" \" << U_j[2] << \" \" << U_j[3]\n              << std::endl;\n    const auto rd_i = riemann_data(U_i);\n    const auto rd_j = riemann_data(U_j);\n    const auto lambda_max = riemann_solver.compute(rd_i, rd_j);\n    std::cout << lambda_max << std::endl;\n  };\n\n  const auto set_covolume = [&](const double covolume) {\n    /*\n     * Set the interpolatory covolume by selecting an equation of state\n     * with a covolume.\n     */\n    std::stringstream parameters;\n    parameters << \"subsection HyperbolicSystem\\n\"\n               << \"set equation of state = van der waals\\n\"\n               << \"subsection van der waals\\n\"\n               << \"set covolume b = \" << std::to_string(covolume) << \"\\n\"\n               << \"end\\n\"\n               << \"end\\n\"\n               << std::endl;\n    ParameterAcceptor::initialize(parameters);\n  };\n\n  std::cout << std::setprecision(16);\n  std::cout << std::scientific;\n\n  /*\n   * Test vectors for ideal gas with gamma = 1.4:\n   */\n\n  /* Leblanc:*/\n  test({1., 0., 2. / 30., 7. / 5.}, {1.e-3, 0., 2. / 3. * 1.e-10, 7. / 5.});\n  /* Sod:*/\n  test({1., 0., 1., 7. / 5.}, {0.125, 0., 0.1, 7. / 5.});\n  /* Lax:*/\n  test({0.445, 0.698, 3.528, 7. / 5.}, {0.5, 0., 0.571, 7. / 5.});\n  /* Fast shock case 1 (paper, section 5.2): */\n  test({1., 1.e1, 1.e3, 7. / 5.}, {1., 10., 0.01, 7. / 5.});\n  /* Fast shock case 2 (paper, section 5.2): */\n  test({5.99924, 19.5975, 460.894, 7. / 5.},\n       {5.99242, -6.19633, 46.0950, 7. / 5.});\n  /* Fast expansion and slow shock, case 1 (Paper, section 5.1) */\n  test({1., 0., 0.01, 7. / 5.}, {1., 0., 1.e2, 7. / 5.});\n  /* Fast expansion and slow shock, case 2 (Paper, section 5.1) */\n  test({1., -1., 0.01, 7. / 5.}, {1., -1., 1.e2, 7. / 5.});\n  /* Fast expansion and slow shock, case 3 (Paper, section 5.1) */\n  test({1., -2.18, 0.01, 7. / 5.}, {1., -2.18, 100., 7. / 5.});\n  /* Case 9:*/\n  test({1.0e-2, 0., 1.0e-2, 7. / 5.}, {1.e3, 0., 1.e3, 7. / 5.});\n  /* Case 10:*/\n  test({1.0, 2.18, 1.e2, 7. / 5.}, {1.0, 2.18, 0.01, 7. / 5.});\n\n  /*\n   * States with non-constant gamma values:\n   */\n\n  /* Shock-shock */\n  set_covolume(0.003);\n  test({1.5, 100., 22., 2.0041781532448066}, {7., 0., 12., 5.7237635705670113});\n\n  /* Shock-expansion */\n  set_covolume(0.003);\n  test({1.5, 0., 22., 2.0041781532448066}, {7., 0., 12., 5.7237635705670113});\n\n  /* Mie-Gruneisen: shock-expansion 1, gamma_min != gamma_m */\n  set_covolume(0.);\n  test({3500., 20., 2.3e10, 118.01508858712090},\n       {2400., 0., 1.5e11, 2.8761770391786854});\n\n  /* Mie-Gruneisen: shock-expansion 2, gamma_min = gamma_m */\n  set_covolume(0.);\n  test({3500., 20., 2.3e10, 118.01508858712090},\n       {3300., 0., 2.2e10, 8.2392709087064375});\n\n  /* Mie-Gruneisen: shock-expansion 3, gamma_min = gamma_m */\n  set_covolume(0.);\n  test({3500., 20., 2.3e10, 118.01508858712090},\n       {3., 0., 2.2e6, 1.0453481734270629});\n\n  /* Mie-Gruneisen: shock-expansion 4, gamma_min = gamma_m */\n  set_covolume(0.);\n  test({350., 20., 2.3e5, 1.0000474957444776},\n       {3., 0., 2.2e6, 1.0453481734270629});\n\n  /* Mie-Gruneisen: shock-expansion 5, gamma_min != gamma_m */\n  set_covolume(0.);\n  test({15., 20., 7.3e8, 2.2145329586703819},\n       {500., 0., 2.2e9, 1.2899388697970200});\n\n  /*\n   * States with crazy two-rarefaction pressure:\n   */\n\n  test({1., 300., 1, 1.4}, {0.125, -300., 0.1, 1.4});\n\n  /*\n   * States with crazy gamma values:\n   */\n\n  test({1., 0., 2. / 30., 2.99}, {1.e-3, 0., 2. / 3. * 1.e-10, 1.40});\n\n  test({1., 0., 2. / 30., 1.01}, {1.e-3, 0., 2. / 3. * 1.e-10, 1.40});\n\n  test({1., 0., 2. / 30., 2.96}, {1.e-3, 0., 2. / 3. * 1.e-10, 2.99});\n\n  test({1., 0., 2. / 30., 40.0}, {1.e-3, 0., 2. / 3. * 1.e-10, 1.001});\n\n  return 0;\n}\n"
  },
  {
    "path": "tests/euler_aeos/riemann_solver.threads=2.output",
    "content": "\n1.0000000000000000e+00 0.0000000000000000e+00 6.6666666666666666e-02 1.3999999999999999e+00\n1.0000000000000000e-03 0.0000000000000000e+00 6.6666666666666669e-11 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 6.6666666666666666e-02\ngamma_left: 1.3999999999999999e+00\na_left: 3.0550504633038933e-01\nrho_right: 1.0000000000000000e-03\nu_right: 0.0000000000000000e+00\np_right: 6.6666666666666669e-11\ngamma_right: 1.3999999999999999e+00\na_right: 3.0550504633038936e-04\nRS p_1_tilde  = 5.8723550288294901e-02\nRS p_2_tilde  = 5.8723550288294901e-02\nSS p_1_tilde  = 5.8723550288294839e-02\nSS p_2_tilde  = 1.8962827388271815e-03\n   p^*_debug  = 5.8723550288294901e-02\n   phi(p_*_d) = 6.9680132444205416e+00\n-> lambda_deb = 8.3945375309952137e+00\nIN p_*_tilde  = 5.8723550288294901e-02\nSS p_2_tilde  = 1.8962827388271815e-03\n   p^*_tilde  = 5.8723550288294901e-02\n   phi(p_*_t) = 6.9680132444205416e+00\n-> lambda_max = 8.3945375309952137e+00\n\n8.3945375309952137e+00\n\n1.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e+00 1.3999999999999999e+00\n1.2500000000000000e-01 0.0000000000000000e+00 1.0000000000000001e-01 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 1.0000000000000000e+00\ngamma_left: 1.3999999999999999e+00\na_left: 1.1832159566199232e+00\nrho_right: 1.2500000000000000e-01\nu_right: 0.0000000000000000e+00\np_right: 1.0000000000000001e-01\ngamma_right: 1.3999999999999999e+00\na_right: 1.0583005244258361e+00\nRS p_1_tilde  = 3.0676664667059678e-01\nRS p_2_tilde  = 3.0676664667059678e-01\nSS p_1_tilde  = 3.0676664667059667e-01\nSS p_2_tilde  = 3.2333029409004022e-01\n   p^*_debug  = 3.0676664667059678e-01\n   phi(p_*_d) = 1.9786963438474081e-02\n-> lambda_deb = 1.7620896140769142e+00\nIN p_*_tilde  = 3.0676664667059678e-01\nSS p_2_tilde  = 3.2333029409004022e-01\n   p^*_tilde  = 3.0676664667059678e-01\n   phi(p_*_t) = 1.9786963438474081e-02\n-> lambda_max = 1.7620896140769142e+00\n\n1.7620896140769142e+00\n\n4.4500000000000001e-01 6.9799999999999995e-01 3.5280000000000000e+00 1.3999999999999999e+00\n5.0000000000000000e-01 0.0000000000000000e+00 5.7099999999999995e-01 1.3999999999999999e+00\nrho_left: 4.4500000000000001e-01\nu_left: 6.9799999999999995e-01\np_left: 3.5280000000000000e+00\ngamma_left: 1.3999999999999999e+00\na_left: 3.3315650740600322e+00\nrho_right: 5.0000000000000000e-01\nu_right: 0.0000000000000000e+00\np_right: 5.7099999999999995e-01\ngamma_right: 1.3999999999999999e+00\na_right: 1.2644366334458994e+00\nRS p_1_tilde  = 2.5096631320093397e+00\nRS p_2_tilde  = 2.5096631320093397e+00\nSS p_1_tilde  = 2.5096631320093410e+00\nSS p_2_tilde  = 2.4778604535394879e+00\n   p^*_debug  = 2.5096631320093397e+00\n   phi(p_*_d) = 6.1653197162831685e-02\n-> lambda_deb = 2.6335650740600323e+00\nIN p_*_tilde  = 2.5096631320093397e+00\nSS p_2_tilde  = 2.4778604535394879e+00\n   p^*_tilde  = 2.5096631320093397e+00\n   phi(p_*_t) = 6.1653197162831685e-02\n-> lambda_max = 2.6335650740600323e+00\n\n2.6335650740600323e+00\n\n1.0000000000000000e+00 1.0000000000000000e+01 1.0000000000000000e+03 1.3999999999999999e+00\n1.0000000000000000e+00 1.0000000000000000e+01 1.0000000000000000e-02 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 1.0000000000000000e+01\np_left: 1.0000000000000000e+03\ngamma_left: 1.3999999999999999e+00\na_left: 3.7416573867739416e+01\nrho_right: 1.0000000000000000e+00\nu_right: 1.0000000000000000e+01\np_right: 1.0000000000000000e-02\ngamma_right: 1.3999999999999999e+00\na_right: 1.1832159566199231e-01\nRS p_1_tilde  = 9.1244932710571970e+02\nRS p_2_tilde  = 9.1244932710571970e+02\nSS p_1_tilde  = 9.1244932710571993e+02\nSS p_2_tilde  = 4.8074609902285869e+02\n   p^*_debug  = 9.1244932710571970e+02\n   phi(p_*_d) = 2.5141795977374755e+01\n-> lambda_deb = 4.3089895625807941e+01\nIN p_*_tilde  = 9.1244932710571970e+02\nSS p_2_tilde  = 4.8074609902285869e+02\n   p^*_tilde  = 9.1244932710571970e+02\n   phi(p_*_t) = 2.5141795977374755e+01\n-> lambda_max = 4.3089895625807941e+01\n\n4.3089895625807941e+01\n\n5.9992400000000004e+00 1.9597500000000000e+01 4.6089400000000001e+02 1.3999999999999999e+00\n5.9924200000000001e+00 -6.1963299999999997e+00 4.6094999999999999e+01 1.3999999999999999e+00\nrho_left: 5.9992400000000004e+00\nu_left: 1.9597500000000000e+01\np_left: 4.6089400000000001e+02\ngamma_left: 1.3999999999999999e+00\na_left: 1.0370899528836672e+01\nrho_right: 5.9924200000000001e+00\nu_right: -6.1963299999999997e+00\np_right: 4.6094999999999999e+01\ngamma_right: 1.3999999999999999e+00\na_right: 3.2816314493370298e+00\nRS p_1_tilde  = 2.3226554570159969e+03\nRS p_2_tilde  = 2.3226554570159969e+03\nSS p_1_tilde  = 2.3226554570159974e+03\nSS p_2_tilde  = 1.7599311105107934e+03\n   p^*_debug  = 1.7599311105107934e+03\n   phi(p_*_d) = 7.0445120003856943e-01\n-> lambda_deb = 1.2617757915202827e+01\nIN p_*_tilde  = 2.3226554570159969e+03\nSS p_2_tilde  = 1.7599311105107934e+03\n   p^*_tilde  = 1.7599311105107934e+03\n   phi(p_*_t) = 7.0445120003856943e-01\n-> lambda_max = 1.2617757915202827e+01\n\n1.2617757915202827e+01\n\n1.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e-02 1.3999999999999999e+00\n1.0000000000000000e+00 0.0000000000000000e+00 1.0000000000000000e+02 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 1.0000000000000000e-02\ngamma_left: 1.3999999999999999e+00\na_left: 1.1832159566199231e-01\nrho_right: 1.0000000000000000e+00\nu_right: 0.0000000000000000e+00\np_right: 1.0000000000000000e+02\ngamma_right: 1.3999999999999999e+00\na_right: 1.1832159566199232e+01\nRS p_1_tilde  = 8.2983069275580775e+01\nRS p_2_tilde  = 8.2983069275580775e+01\nSS p_1_tilde  = 8.2983069275580775e+01\nSS p_2_tilde  = 4.8079470435532315e+01\n   p^*_debug  = 8.2983069275580775e+01\n   phi(p_*_d) = 6.7590391906237013e+00\n-> lambda_deb = 1.1832159566199232e+01\nIN p_*_tilde  = 8.2983069275580775e+01\nSS p_2_tilde  = 4.8079470435532315e+01\n   p^*_tilde  = 8.2983069275580775e+01\n   phi(p_*_t) = 6.7590391906237013e+00\n-> lambda_max = 1.1832159566199232e+01\n\n1.1832159566199232e+01\n\n1.0000000000000000e+00 -1.0000000000000000e+00 1.0000000000000000e-02 1.3999999999999999e+00\n1.0000000000000000e+00 -1.0000000000000000e+00 1.0000000000000000e+02 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: -1.0000000000000000e+00\np_left: 1.0000000000000000e-02\ngamma_left: 1.3999999999999999e+00\na_left: 1.1832159566199231e-01\nrho_right: 1.0000000000000000e+00\nu_right: -1.0000000000000000e+00\np_right: 1.0000000000000000e+02\ngamma_right: 1.3999999999999999e+00\na_right: 1.1832159566199232e+01\nRS p_1_tilde  = 8.2983069275580775e+01\nRS p_2_tilde  = 8.2983069275580775e+01\nSS p_1_tilde  = 8.2983069275580775e+01\nSS p_2_tilde  = 4.8079470435532315e+01\n   p^*_debug  = 8.2983069275580775e+01\n   phi(p_*_d) = 6.7590391906237013e+00\n-> lambda_deb = 1.0979062237038955e+01\nIN p_*_tilde  = 8.2983069275580775e+01\nSS p_2_tilde  = 4.8079470435532315e+01\n   p^*_tilde  = 8.2983069275580775e+01\n   phi(p_*_t) = 6.7590391906237013e+00\n-> lambda_max = 1.0979062237038955e+01\n\n1.0979062237038955e+01\n\n1.0000000000000000e+00 -2.1800000000000002e+00 1.0000000000000000e-02 1.3999999999999999e+00\n1.0000000000000000e+00 -2.1800000000000002e+00 1.0000000000000000e+02 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: -2.1800000000000002e+00\np_left: 1.0000000000000000e-02\ngamma_left: 1.3999999999999999e+00\na_left: 1.1832159566199231e-01\nrho_right: 1.0000000000000000e+00\nu_right: -2.1800000000000002e+00\np_right: 1.0000000000000000e+02\ngamma_right: 1.3999999999999999e+00\na_right: 1.1832159566199232e+01\nRS p_1_tilde  = 8.2983069275580775e+01\nRS p_2_tilde  = 8.2983069275580775e+01\nSS p_1_tilde  = 8.2983069275580775e+01\nSS p_2_tilde  = 4.8079470435532315e+01\n   p^*_debug  = 8.2983069275580775e+01\n   phi(p_*_d) = 6.7590391906237013e+00\n-> lambda_deb = 1.2159062237038954e+01\nIN p_*_tilde  = 8.2983069275580775e+01\nSS p_2_tilde  = 4.8079470435532315e+01\n   p^*_tilde  = 8.2983069275580775e+01\n   phi(p_*_t) = 6.7590391906237013e+00\n-> lambda_max = 1.2159062237038954e+01\n\n1.2159062237038954e+01\n\n1.0000000000000000e-02 0.0000000000000000e+00 1.0000000000000000e-02 1.3999999999999999e+00\n1.0000000000000000e+03 0.0000000000000000e+00 1.0000000000000000e+03 1.3999999999999999e+00\nrho_left: 1.0000000000000000e-02\nu_left: 0.0000000000000000e+00\np_left: 1.0000000000000000e-02\ngamma_left: 1.3999999999999999e+00\na_left: 1.1832159566199232e+00\nrho_right: 1.0000000000000000e+03\nu_right: 0.0000000000000000e+00\np_right: 1.0000000000000000e+03\ngamma_right: 1.3999999999999999e+00\na_right: 1.1832159566199232e+00\nRS p_1_tilde  = 3.7200525124077177e-01\nRS p_2_tilde  = 3.7200525124077177e-01\nSS p_1_tilde  = 3.7200525124077177e-01\nSS p_2_tilde  = 2.9291270455561289e+00\n   p^*_debug  = 3.7200525124077177e-01\n   phi(p_*_d) = 1.4047089424401182e+00\n-> lambda_deb = 6.6963146691962301e+00\nIN p_*_tilde  = 3.7200525124077177e-01\nSS p_2_tilde  = 2.9291270455561289e+00\n   p^*_tilde  = 3.7200525124077177e-01\n   phi(p_*_t) = 1.4047089424401182e+00\n-> lambda_max = 6.6963146691962301e+00\n\n6.6963146691962301e+00\n\n1.0000000000000000e+00 2.1800000000000002e+00 1.0000000000000000e+02 1.3999999999999999e+00\n1.0000000000000000e+00 2.1800000000000002e+00 1.0000000000000000e-02 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 2.1800000000000002e+00\np_left: 1.0000000000000000e+02\ngamma_left: 1.3999999999999999e+00\na_left: 1.1832159566199232e+01\nrho_right: 1.0000000000000000e+00\nu_right: 2.1800000000000002e+00\np_right: 1.0000000000000000e-02\ngamma_right: 1.3999999999999999e+00\na_right: 1.1832159566199231e-01\nRS p_1_tilde  = 8.2983069275580775e+01\nRS p_2_tilde  = 8.2983069275580775e+01\nSS p_1_tilde  = 8.2983069275580817e+01\nSS p_2_tilde  = 4.8079470435532315e+01\n   p^*_debug  = 8.2983069275580775e+01\n   phi(p_*_d) = 6.7590391906237013e+00\n-> lambda_deb = 1.2159062237038954e+01\nIN p_*_tilde  = 8.2983069275580775e+01\nSS p_2_tilde  = 4.8079470435532315e+01\n   p^*_tilde  = 8.2983069275580775e+01\n   phi(p_*_t) = 6.7590391906237013e+00\n-> lambda_max = 1.2159062237038954e+01\n\n1.2159062237038954e+01\n\n1.5000000000000000e+00 1.0000000000000000e+02 2.2000000000000000e+01 2.0041781532448066e+00\n7.0000000000000000e+00 0.0000000000000000e+00 1.2000000000000000e+01 5.7237635705670113e+00\nrho_left: 1.5000000000000000e+00\nu_left: 1.0000000000000000e+02\np_left: 2.2000000000000000e+01\ngamma_left: 2.0041781532448066e+00\na_left: 5.4339200034574526e+00\nrho_right: 7.0000000000000000e+00\nu_right: 0.0000000000000000e+00\np_right: 1.2000000000000000e+01\ngamma_right: 5.7237635705670113e+00\na_right: 3.1658555864814155e+00\nRS p_1_tilde  = 4.9365679206103723e+03\nRS p_2_tilde  = 1.7025505863088969e+05\nSS p_1_tilde  = 1.8698827064740786e+05\nSS p_2_tilde  = 1.7870707469526966e+04\n   p^*_debug  = 1.7870707469526966e+04\n   phi(p_*_d) = 1.5971962570955895e+01\n-> lambda_deb = 9.3653527931081371e+01\nIN p_*_tilde  = 1.8905366462119186e+05\nSS p_2_tilde  = 1.7870707469526966e+04\n   p^*_tilde  = 1.7870707469526966e+04\n   phi(p_*_t) = 1.5971962570955895e+01\n-> lambda_max = 9.3653527931081371e+01\n\n9.3653527931081371e+01\n\n1.5000000000000000e+00 0.0000000000000000e+00 2.2000000000000000e+01 2.0041781532448066e+00\n7.0000000000000000e+00 0.0000000000000000e+00 1.2000000000000000e+01 5.7237635705670113e+00\nrho_left: 1.5000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 2.2000000000000000e+01\ngamma_left: 2.0041781532448066e+00\na_left: 5.4339200034574526e+00\nrho_right: 7.0000000000000000e+00\nu_right: 0.0000000000000000e+00\np_right: 1.2000000000000000e+01\ngamma_right: 5.7237635705670113e+00\na_right: 3.1658555864814155e+00\nRS p_1_tilde  = 2.0843455346157853e+01\nRS p_2_tilde  = 2.0894211215041093e+01\nSS p_1_tilde  = 2.0865394227858054e+01\nSS p_2_tilde  = 1.9684207500367314e+01\n   p^*_debug  = 2.0843455346157853e+01\n   phi(p_*_d) = 1.8859790097336063e-01\n-> lambda_deb = 5.4339200034574526e+00\nIN p_*_tilde  = 2.3201232499860932e+01\nSS p_2_tilde  = 1.9684207500367314e+01\n   p^*_tilde  = 2.2000000000000000e+01\n   phi(p_*_t) = 3.6973967876565816e-01\n-> lambda_max = 5.4339200034574526e+00\n\n5.4339200034574526e+00\n\n3.5000000000000000e+03 2.0000000000000000e+01 2.3000000000000000e+10 1.1801508858712090e+02\n2.4000000000000000e+03 0.0000000000000000e+00 1.5000000000000000e+11 2.8761770391786854e+00\nrho_left: 3.5000000000000000e+03\nu_left: 2.0000000000000000e+01\np_left: 2.3000000000000000e+10\ngamma_left: 1.1801508858712090e+02\na_left: 2.7848298422005613e+04\nrho_right: 2.4000000000000000e+03\nu_right: 0.0000000000000000e+00\np_right: 1.5000000000000000e+11\ngamma_right: 2.8761770391786854e+00\na_right: 1.3407500324395589e+04\nRS p_1_tilde  = 1.4025323174221558e+11\nRS p_2_tilde  = 1.4201086622973593e+11\nSS p_1_tilde  = 1.4134908461814151e+11\nSS p_2_tilde  = 1.3209042937602786e+11\n   p^*_debug  = 1.4025323174221558e+11\n   phi(p_*_d) = 3.0684790396076323e+02\n-> lambda_deb = 5.2602099971581149e+04\nIN p_*_tilde  = 1.8212048811813354e+11\nSS p_2_tilde  = 1.3209042937602786e+11\n   p^*_tilde  = 1.5000000000000000e+11\n   phi(p_*_t) = 6.4980180300802624e+02\n-> lambda_max = 5.4153808017174706e+04\n\n5.4153808017174706e+04\n\n3.5000000000000000e+03 2.0000000000000000e+01 2.3000000000000000e+10 1.1801508858712090e+02\n3.3000000000000000e+03 0.0000000000000000e+00 2.2000000000000000e+10 8.2392709087064375e+00\nrho_left: 3.5000000000000000e+03\nu_left: 2.0000000000000000e+01\np_left: 2.3000000000000000e+10\ngamma_left: 1.1801508858712090e+02\na_left: 2.7848298422005613e+04\nrho_right: 3.3000000000000000e+03\nu_right: 0.0000000000000000e+00\np_right: 2.2000000000000000e+10\ngamma_right: 8.2392709087064375e+00\na_right: 7.4113745502915708e+03\nRS p_1_tilde  = 2.2797243514311752e+10\nRS p_2_tilde  = 2.2803021562280891e+10\nSS p_1_tilde  = 2.2753789255105709e+10\nSS p_2_tilde  = 2.2594095966058762e+10\n   p^*_debug  = 2.2797243514311752e+10\n   phi(p_*_d) = 1.0185995765187972e+01\n-> lambda_deb = 2.7828298422005613e+04\nIN p_*_tilde  = 2.6982699105417969e+10\nSS p_2_tilde  = 2.2594095966058762e+10\n   p^*_tilde  = 2.3000000000000000e+10\n   phi(p_*_t) = 2.0375926203897123e+01\n-> lambda_max = 2.7828298422005613e+04\n\n2.7828298422005613e+04\n\n3.5000000000000000e+03 2.0000000000000000e+01 2.3000000000000000e+10 1.1801508858712090e+02\n3.0000000000000000e+00 0.0000000000000000e+00 2.2000000000000000e+06 1.0453481734270629e+00\nrho_left: 3.5000000000000000e+03\nu_left: 2.0000000000000000e+01\np_left: 2.3000000000000000e+10\ngamma_left: 1.1801508858712090e+02\na_left: 2.7848298422005613e+04\nrho_right: 3.0000000000000000e+00\nu_right: 0.0000000000000000e+00\np_right: 2.2000000000000000e+06\ngamma_right: 1.0453481734270629e+00\na_right: 8.7555048998511757e+02\nRS p_1_tilde  = 1.5426478427707598e+10\nRS p_2_tilde  = 3.9395674823204307e+06\nSS p_1_tilde  = 2.4220969328407627e+06\nSS p_2_tilde  = 6.4989009637691483e+07\n   p^*_debug  = 3.9395674823204307e+06\n   phi(p_*_d) = 7.7814794391570103e+00\n-> lambda_deb = 2.7828298422005613e+04\nIN p_*_tilde  = 3.9462420292156078e+06\nSS p_2_tilde  = 6.4989009637691483e+07\n   p^*_tilde  = 3.9462420292156078e+06\n   phi(p_*_t) = 9.2777941834719968e+00\n-> lambda_max = 2.7828298422005613e+04\n\n2.7828298422005613e+04\n\n3.5000000000000000e+02 2.0000000000000000e+01 2.3000000000000000e+05 1.0000474957444776e+00\n3.0000000000000000e+00 0.0000000000000000e+00 2.2000000000000000e+06 1.0453481734270629e+00\nrho_left: 3.5000000000000000e+02\nu_left: 2.0000000000000000e+01\np_left: 2.3000000000000000e+05\ngamma_left: 1.0000474957444776e+00\na_left: 2.5635406543140395e+01\nrho_right: 3.0000000000000000e+00\nu_right: 0.0000000000000000e+00\np_right: 2.2000000000000000e+06\ngamma_right: 1.0453481734270629e+00\na_right: 8.7555048998511757e+02\nRS p_1_tilde  = 2.1965659233776061e+06\nRS p_2_tilde  = 8.0326196662183801e+35\nSS p_1_tilde  = 5.2811824081277614e+05\nSS p_2_tilde  = 2.0762723673749219e+06\n   p^*_debug  = 2.1965659233776061e+06\n   phi(p_*_d) = 4.9616166754114815e+01\n-> lambda_deb = 8.7555048998511757e+02\nIN p_*_tilde  = 8.0326196662183801e+35\nSS p_2_tilde  = 2.0762723673749219e+06\n   p^*_tilde  = 2.2000000000000000e+06\n   phi(p_*_t) = 5.0992940972643083e+01\n-> lambda_max = 8.7555048998511757e+02\n\n8.7555048998511757e+02\n\n1.5000000000000000e+01 2.0000000000000000e+01 7.3000000000000000e+08 2.2145329586703819e+00\n5.0000000000000000e+02 0.0000000000000000e+00 2.2000000000000000e+09 1.2899388697970200e+00\nrho_left: 1.5000000000000000e+01\nu_left: 2.0000000000000000e+01\np_left: 7.3000000000000000e+08\ngamma_left: 2.2145329586703819e+00\na_left: 1.0381422702209875e+04\nrho_right: 5.0000000000000000e+02\nu_right: 0.0000000000000000e+00\np_right: 2.2000000000000000e+09\ngamma_right: 1.2899388697970200e+00\na_right: 2.3823792786008880e+03\nRS p_1_tilde  = 1.2200916684437129e+09\nRS p_2_tilde  = 1.2543388113522141e+09\nSS p_1_tilde  = 1.2543388113522141e+09\nSS p_2_tilde  = 9.8289530031049967e+08\n   p^*_debug  = 1.2200916684437129e+09\n   phi(p_*_d) = 1.5071738421543907e+03\n-> lambda_deb = 1.2640481745232328e+04\nIN p_*_tilde  = 1.2543388113522141e+09\nSS p_2_tilde  = 9.8289530031049967e+08\n   p^*_tilde  = 1.2543388113522141e+09\n   phi(p_*_t) = 1.7043608770030055e+03\n-> lambda_max = 1.2784585780441452e+04\n\n1.2784585780441452e+04\n\n1.0000000000000000e+00 3.0000000000000000e+02 1.0000000000000000e+00 1.3999999999999999e+00\n1.2500000000000000e-01 -3.0000000000000000e+02 1.0000000000000001e-01 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 3.0000000000000000e+02\np_left: 1.0000000000000000e+00\ngamma_left: 1.3999999999999999e+00\na_left: 1.1832159566199232e+00\nrho_right: 1.2500000000000000e-01\nu_right: -3.0000000000000000e+02\np_right: 1.0000000000000001e-01\ngamma_right: 1.3999999999999999e+00\na_right: 1.0583005244258361e+00\nRS p_1_tilde  = 4.4009424225516168e+11\nRS p_2_tilde  = 4.4009424225516168e+11\nSS p_1_tilde  = 4.4009424225516156e+11\nSS p_2_tilde  = 3.1034863706855802e+04\n   p^*_debug  = 3.1034863706855802e+04\n   phi(p_*_d) = 1.5672283636362181e+01\n-> lambda_deb = 2.4583408796612875e+02\nIN p_*_tilde  = 4.4009424225516168e+11\nSS p_2_tilde  = 3.1034863706855802e+04\n   p^*_tilde  = 3.1034863706855802e+04\n   phi(p_*_t) = 1.5672283636362181e+01\n-> lambda_max = 2.4583408796612875e+02\n\n2.4583408796612875e+02\n\n1.0000000000000000e+00 0.0000000000000000e+00 6.6666666666666666e-02 2.9900000000000002e+00\n1.0000000000000000e-03 0.0000000000000000e+00 6.6666666666666669e-11 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 6.6666666666666666e-02\ngamma_left: 2.9900000000000002e+00\na_left: 4.4646761734008583e-01\nrho_right: 1.0000000000000000e-03\nu_right: 0.0000000000000000e+00\np_right: 6.6666666666666669e-11\ngamma_right: 1.3999999999999999e+00\na_right: 3.0550504633038936e-04\nRS p_1_tilde  = 5.5624820417800008e-02\nRS p_2_tilde  = 2.0851010080017713e+06\nSS p_1_tilde  = 4.2068839637523107e-02\nSS p_2_tilde  = 1.3093308231259685e-03\n   p^*_debug  = 5.5624820417800008e-02\n   phi(p_*_d) = 6.7821386915218982e+00\n-> lambda_deb = 8.1700541316868502e+00\nIN p_*_tilde  = 2.3993624659613278e+06\nSS p_2_tilde  = 1.3093308231259685e-03\n   p^*_tilde  = 6.6666666666666666e-02\n   phi(p_*_t) = 7.4535599169246085e+00\n-> lambda_max = 8.9442719107445150e+00\n\n8.9442719107445150e+00\n\n1.0000000000000000e+00 0.0000000000000000e+00 6.6666666666666666e-02 1.0100000000000000e+00\n1.0000000000000000e-03 0.0000000000000000e+00 6.6666666666666669e-11 1.3999999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 6.6666666666666666e-02\ngamma_left: 1.0100000000000000e+00\na_left: 2.5948667274704751e-01\nrho_right: 1.0000000000000000e-03\nu_right: 0.0000000000000000e+00\np_right: 6.6666666666666669e-11\ngamma_right: 1.3999999999999999e+00\na_right: 3.0550504633038936e-04\nRS p_1_tilde  = 6.6415756388980215e-02\nRS p_2_tilde  = 6.6623858660829760e-02\nSS p_1_tilde  = 6.6623858660830246e-02\nSS p_2_tilde  = 2.2213713553365356e-03\n   p^*_debug  = 6.6415756388980215e-02\n   phi(p_*_d) = 7.4385516197631727e+00\n-> lambda_deb = 8.9274244707031603e+00\nIN p_*_tilde  = 6.6623858660829760e-02\nSS p_2_tilde  = 2.2213713553365356e-03\n   p^*_tilde  = 6.6623858660829760e-02\n   phi(p_*_t) = 7.4510014677556553e+00\n-> lambda_max = 8.9413998012799460e+00\n\n8.9413998012799460e+00\n\n1.0000000000000000e+00 0.0000000000000000e+00 6.6666666666666666e-02 2.9600000000000000e+00\n1.0000000000000000e-03 0.0000000000000000e+00 6.6666666666666669e-11 2.9900000000000002e+00\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 6.6666666666666666e-02\ngamma_left: 2.9600000000000000e+00\na_left: 4.4422216663887154e-01\nrho_right: 1.0000000000000000e-03\nu_right: 0.0000000000000000e+00\np_right: 6.6666666666666669e-11\ngamma_right: 2.9900000000000002e+00\na_right: 4.4646761734008588e-04\nRS p_1_tilde  = 9.8117204055763567e-03\nRS p_2_tilde  = 1.0210027643897120e-02\nSS p_1_tilde  = 8.9882488242153998e-03\nSS p_2_tilde  = 1.6869545981586475e-03\n   p^*_debug  = 9.8117204055763567e-03\n   phi(p_*_d) = 2.0047626369527043e+00\n-> lambda_deb = 4.4242945511638538e+00\nIN p_*_tilde  = 1.1762504450935046e-02\nSS p_2_tilde  = 1.6869545981586475e-03\n   p^*_tilde  = 1.1762504450935046e-02\n   phi(p_*_t) = 2.2301102398025097e+00\n-> lambda_max = 4.8441920323154770e+00\n\n4.8441920323154770e+00\n\n1.0000000000000000e+00 0.0000000000000000e+00 6.6666666666666666e-02 4.0000000000000000e+01\n1.0000000000000000e-03 0.0000000000000000e+00 6.6666666666666669e-11 1.0009999999999999e+00\nrho_left: 1.0000000000000000e+00\nu_left: 0.0000000000000000e+00\np_left: 6.6666666666666666e-02\ngamma_left: 4.0000000000000000e+01\na_left: 1.6329931618554521e+00\nrho_right: 1.0000000000000000e-03\nu_right: 0.0000000000000000e+00\np_right: 6.6666666666666669e-11\ngamma_right: 1.0009999999999999e+00\na_right: 2.5832795693330086e-04\nRS p_1_tilde  = 6.5458747897678402e-02\nRS p_2_tilde  = 2.6665134777986636e+120\nSS p_1_tilde  = 5.5599488097791917e-10\nSS p_2_tilde  = 3.3175752067092170e-04\n   p^*_debug  = 6.5458747897678402e-02\n   phi(p_*_d) = 8.0878929864984261e+00\n-> lambda_deb = 8.0926804750750279e+00\nIN p_*_tilde  = 2.6769176840345778e+120\nSS p_2_tilde  = 3.3175752067092170e-04\n   p^*_tilde  = 6.6666666666666666e-02\n   phi(p_*_t) = 8.1629253248067162e+00\n-> lambda_max = 8.1670067956402086e+00\n\n8.1670067956402086e+00\n"
  },
  {
    "path": "tests/euler_aeos/verification-isentropic_vortex-PGE-2d-erk33-l5.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-euler-l5\n\n  set enable compute error      = true\n\n  set final time                = 2.0\n\n  set timer granularity         = 2.0\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension                = 2\n  set equation                 = euler aeos\n  set compute strict bounds    = true\n  set equation of state        = polytropic gas\n  subsection polytropic gas\n    set gamma = 1.4\n  end\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 5\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.2\n  set cfl max            = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler_aeos/verification-isentropic_vortex-PGE-2d-erk33-l5.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler aeos« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 1089\nt     = 2.005478356214783\nLinf  = 0.05684737300145233\nL1    = 0.003476204460165304\nL2    = 0.008732194944588375\n"
  },
  {
    "path": "tests/euler_aeos/verification-isentropic_vortex-PGE-2d-erk33-l6.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-euler-l6\n\n  set enable compute error      = true\n\n  set final time                = 2.0\n\n  set timer granularity         = 2.0\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension                = 2\n  set equation                 = euler aeos\n  set compute strict bounds    = true\n  set equation of state        = polytropic gas\n  subsection polytropic gas\n    set gamma = 1.4\n  end\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.2\n  set cfl max            = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler_aeos/verification-isentropic_vortex-PGE-2d-erk33-l6.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler aeos« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 4225\nt     = 2.005478362239249\nLinf  = 0.005459599391126781\nL1    = 0.0004014079936683995\nL2    = 0.0009434933616712649\n"
  },
  {
    "path": "tests/euler_aeos/verification-isentropic_vortex-PGE-2d-erk33-l7.mpirun=4.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler aeos« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 16641\nt     = 2.005478363783097\nLinf  = 0.0006421458507517179\nL1    = 4.963810197315999e-05\nL2    = 0.0001163746113900111\n"
  },
  {
    "path": "tests/euler_aeos/verification-isentropic_vortex-PGE-2d-erk33-l7.mpirun=8.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler aeos« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 16641\nt     = 2.005478363783098\nLinf  = 0.000642145850751763\nL1    = 4.963810197325961e-05\nL2    = 0.0001163746113899151\n"
  },
  {
    "path": "tests/euler_aeos/verification-isentropic_vortex-PGE-2d-erk33-l7.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-euler-l7\n\n  set enable compute error      = true\n\n  set final time                = 2.0\n\n  set timer granularity         = 2.0\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension                = 2\n  set equation                 = euler aeos\n  set compute strict bounds    = true\n  set equation of state        = polytropic gas\n  subsection polytropic gas\n    set gamma = 1.4\n  end\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 7\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.2\n  set cfl max            = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler_aeos/verification-isentropic_vortex-PGE-2d-ssprk33-l5.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-euler-l5\n\n  set enable compute error      = true\n\n  set final time                = 2.0\n\n  set timer granularity         = 2.0\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension                = 2\n  set equation                 = euler aeos\n  set compute strict bounds    = true\n  set equation of state        = polytropic gas\n  subsection polytropic gas\n    set gamma = 1.4\n  end\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 5\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.2\n  set cfl max            = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = ssprk 33\nend\n"
  },
  {
    "path": "tests/euler_aeos/verification-isentropic_vortex-PGE-2d-ssprk33-l5.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler aeos« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 1089\nt     = 2.00547835661566\nLinf  = 0.05677967441651505\nL1    = 0.003472894621127225\nL2    = 0.008728325603443068\n"
  },
  {
    "path": "tests/euler_aeos/verification-isentropic_vortex-PGE-2d-ssprk33-l6.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-euler-l6\n\n  set enable compute error      = true\n\n  set final time                = 2.0\n\n  set timer granularity         = 2.0\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension                = 2\n  set equation                 = euler aeos\n  set compute strict bounds    = true\n  set equation of state        = polytropic gas\n  subsection polytropic gas\n    set gamma = 1.4\n  end\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.2\n  set cfl max            = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = ssprk 33\nend\n"
  },
  {
    "path": "tests/euler_aeos/verification-isentropic_vortex-PGE-2d-ssprk33-l6.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler aeos« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 4225\nt     = 2.001764513464058\nLinf  = 0.005445539988176942\nL1    = 0.0004004235491088216\nL2    = 0.0009414755589252915\n"
  },
  {
    "path": "tests/euler_aeos/verification-isentropic_vortex-PGE-2d-ssprk33-l7.mpirun=4.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler aeos« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 16641\nt     = 2.001764514969715\nLinf  = 0.0006406622146636022\nL1    = 4.953578298180296e-05\nL2    = 0.0001161423526034969\n"
  },
  {
    "path": "tests/euler_aeos/verification-isentropic_vortex-PGE-2d-ssprk33-l7.mpirun=8.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler aeos« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 16641\nt     = 2.001764514969715\nLinf  = 0.00064066221466615\nL1    = 4.953578298173302e-05\nL2    = 0.0001161423526037025\n"
  },
  {
    "path": "tests/euler_aeos/verification-isentropic_vortex-PGE-2d-ssprk33-l7.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-euler-l7\n\n  set enable compute error      = true\n\n  set final time                = 2.0\n\n  set timer granularity         = 2.0\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension                = 2\n  set equation                 = euler aeos\n  set compute strict bounds    = true\n  set equation of state        = polytropic gas\n  subsection polytropic gas\n    set gamma = 1.4\n  end\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 7\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.2\n  set cfl max            = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = ssprk 33\nend\n"
  },
  {
    "path": "tests/euler_aeos/verification-leblanc-PGE-1d-erk33-l6-strict.mpirun=4.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler aeos« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 1601\nt     = 0.6672156833726536\nLinf  = 0.2565920535637815\nL1    = 0.01172977710684317\nL2    = 0.03029397951275109\n"
  },
  {
    "path": "tests/euler_aeos/verification-leblanc-PGE-1d-erk33-l6-strict.prm",
    "content": "subsection A - TimeLoop\n  set basename             = leblanc-erk33\n\n  set enable compute error = true\n  set error quantities     = rho, m, E\n\n  set final time           = 0.66666666666667\n  set timer granularity    = 0.66666666666667\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension             = 1\n  set equation              = euler aeos\n  set compute strict bounds = true\n  set equation of state     = polytropic gas\n  subsection polytropic gas\n    set gamma    = 1.66666666666667\n  end\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = 0\n    set position top right        = 1\n    set subdivisions x            = 25\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = leblanc\n\n  set direction     = 1\n  # for cG put the discontinuity right in the center of a cell\n  set position      = 0.326732673267\nend\n\n\nsubsection F - HyperbolicModule\n  subsection limiter\n    set iterations            = 2\n    set newton max iterations = 2\n    set newton tolerance      = 1e-10\n    set relaxation factor     = 8\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.75\n  set cfl max               = 0.75\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler_aeos/verification-leblanc-PGE-1d-erk33-l6.mpirun=4.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler aeos« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 1601\nt     = 0.6666667374733917\nLinf  = 0.2157758337954887\nL1    = 0.01209329301782065\nL2    = 0.02606498438294566\n"
  },
  {
    "path": "tests/euler_aeos/verification-leblanc-PGE-1d-erk33-l6.prm",
    "content": "subsection A - TimeLoop\n  set basename             = leblanc-erk33\n\n  set enable compute error = true\n  set error quantities     = rho, m, E\n\n  set final time           = 0.66666666666667\n  set timer granularity    = 0.66666666666667\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension             = 1\n  set equation              = euler aeos\n  set compute strict bounds = false\n  set equation of state     = polytropic gas\n  subsection polytropic gas\n    set gamma = 1.66666666666667\n  end\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = 0\n    set position top right        = 1\n    set subdivisions x            = 25\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = leblanc\n\n  set direction     = 1\n  # for cG put the discontinuity right in the center of a cell\n  set position      = 0.326732673267\nend\n\n\nsubsection F - HyperbolicModule\n  subsection indicator\n    set evc factor = 0.\n  end\n\n  subsection limiter\n    set iterations            = 2\n    set newton max iterations = 2\n    set newton tolerance      = 1e-10\n    set relaxation factor     = 8\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.10\n  set cfl max               = 0.10\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler_aeos/verification-nasg_smooth_wave-1d-erk33-l9.prm",
    "content": "##\n#\n# Euler AEOS benchmark\n#\n# A 1D benchmark configuration consisting of two \"strong rarefaction waves\". See section  \"Strong rarefaction waves\" in [1] for details.\n#\n# This configuration uses an explicit ERK(3, 3, 1) timestepping. Expected\n# results are reported in verification-nasg_smooth_wave-1d-erk33-l9.output.\n# If used with the `prm/create_convergence_tables` script, the output of the prm file should give 3rd order accuracy in the L^1 norm.\n#\n# [1] Nearly-incompressible flow: The Tammann equation of state. http://www.clawpack.org/riemann_book/html/Euler_Tammann.html \n##\nsubsection A - TimeLoop\n  set basename                      = nasg_smooth_wave_1d-erk33.l9\n  set enable compute error          = true\n  set error normalize               = true\n  set error quantities              = rho, m, E\n\n  set enforce final time            = true\n  set final time                    = 0.6\n  set timer granularity             = 0.6\n\n  set terminal update interval      = 0\nend\n\nsubsection B - Equation\n  set dimension                     = 1\n  set equation                      = euler aeos\n  set equation of state             = noble abel stiffened gas\n  set compute strict bounds         = true\n\n  subsection noble abel stiffened gas\n    set gamma                              = 1.4\n    set gas constant R                     = 287.053\n    set covolume b                         = 0.01\n    set reference specific internal energy = 0\n    set reference pressure                 = 1\n  end\nend\n\nsubsection C - Discretization\n  set geometry            = rectangular domain\n  set mesh refinement     = 9\n\n  subsection rectangular domain\n    set boundary condition left  = dirichlet\n    set boundary condition right = dirichlet\n    set position bottom left     = 0\n    set position top right       = 1\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = smooth wave\n  set position      = 0\n\n  subsection smooth wave\n    set mach number        = 1\n    set reference density  = 1\n    set reference pressure = -0.5\n  end\nend\n\nsubsection F - HyperbolicModule\n  subsection limiter\n    set iterations            = 2\n    set newton max iterations = 2\n    set newton tolerance      = 1e-10\n    set relaxation factor     = 1\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl max               = 0.50\n  set cfl min               = 0.50\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler_aeos/verification-nasg_smooth_wave-1d-erk33-l9.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler aeos« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 513\nt     = 0.6\nLinf  = 0.001837514299838697\nL1    = 0.0001019149376136227\nL2    = 0.0002964053218802694\n"
  },
  {
    "path": "tests/euler_aeos/verification-rarefaction-PGE-1d-erk33-l6.mpirun=4.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler aeos« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 1601\nt     = 0.3056207720266577\nLinf  = 0.001240817632425219\nL1    = 2.241877418799829e-05\nL2    = 8.124708959839816e-05\n"
  },
  {
    "path": "tests/euler_aeos/verification-rarefaction-PGE-1d-erk33-l6.prm",
    "content": "subsection A - TimeLoop\n  set basename             = leblanc-erk33\n\n  set enable compute error = true\n  set error quantities     = rho, m, E\n\n  set final time           = 0.30558\n  set timer granularity    = 0.30558\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension             = 1\n  set equation              = euler aeos\n  set compute strict bounds = false\n  set equation of state     = polytropic gas\n  subsection polytropic gas\n    set gamma = 1.4\n  end\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = 0\n    set position top right        = 1\n    set subdivisions x            = 25\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = rarefaction\n\n  set direction     = 1\n  set position      = 0.2\nend\n\n\nsubsection F - HyperbolicModule\n  subsection indicator\n    set evc factor = 0.\n  end\n\n  subsection limiter\n    set iterations            = 2\n    set newton max iterations = 2\n    set newton tolerance      = 1e-10\n    set relaxation factor     = 8\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.10\n  set cfl max               = 0.10\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler_aeos/verification-zero_pressure-1d-erk33-l4.prm",
    "content": "subsection A - TimeLoop\n  set basename             = zero_pressure-erk33\n\n  set enable compute error = true\n  set error quantities     = rho, m, E\n\n  set enforce final time        = true\n\n  set final time           = 0.66666666666667\n  set timer granularity    = 0.66666666666667\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension             = 1\n  set equation              = euler aeos\n  set compute strict bounds = true\n  set equation of state     = function\n\n  subsection function\n    set pressure                 = 0.0\n    set specific internal energy = 0.0\n  end\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 4\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = 0\n    set position top right        = 1\n    set subdivisions x            = 25\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = leblanc\n\n  set direction     = 1\n  # for cG put the discontinuity right in the center of a cell\n  set position      = 0.326732673267\nend\n\n\nsubsection F - HyperbolicModule\n  subsection indicator\n    set evc factor = 0.\n  end\n\n  subsection limiter\n    set iterations            = 2\n    set newton max iterations = 2\n    set newton tolerance      = 1e-10\n    set relaxation factor     = 1\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.10\n  set cfl max               = 0.10\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler_aeos/verification-zero_pressure-1d-erk33-l4.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler aeos« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 401\nt     = 0.66666666666667\nLinf  = 2.203619837762918\nL1    = 1.963229218374023\nL2    = 1.978080475933407\n"
  },
  {
    "path": "tests/euler_aeos/verification-zero_pressure-strict-1d-erk33-l4.prm",
    "content": "subsection A - TimeLoop\n  set basename             = zero_pressure-erk33\n\n  set enable compute error = true\n  set error quantities     = rho, m, E\n\n  set enforce final time        = true\n\n  set final time           = 0.66666666666667\n  set timer granularity    = 0.66666666666667\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension             = 1\n  set equation              = euler aeos\n  set compute strict bounds = false\n  set equation of state     = function\n\n  subsection function\n    set pressure                 = 0.0\n    set specific internal energy = 0.0\n  end\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 4\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = 0\n    set position top right        = 1\n    set subdivisions x            = 25\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = leblanc\n\n  set direction     = 1\n  # for cG put the discontinuity right in the center of a cell\n  set position      = 0.326732673267\nend\n\n\nsubsection F - HyperbolicModule\n  subsection indicator\n    set evc factor = 0.\n  end\n\n  subsection limiter\n    set iterations            = 2\n    set newton max iterations = 2\n    set newton tolerance      = 1e-10\n    set relaxation factor     = 1\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.10\n  set cfl max               = 0.10\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler_aeos/verification-zero_pressure-strict-1d-erk33-l4.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler aeos« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 401\nt     = 0.66666666666667\nLinf  = 2.203619837762918\nL1    = 1.963229218374023\nL2    = 1.978080475933407\n"
  },
  {
    "path": "tests/euler_barotropic/CMakeLists.txt",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2022 - 2025 by the ryujin authors\n##\n\nset(EQUATION euler_barotropic)\n\ninclude_directories(\n  ${CMAKE_BINARY_DIR}/source/\n  ${CMAKE_SOURCE_DIR}/source/${EQUATION}\n  ${CMAKE_SOURCE_DIR}/source/\n  )\n\nset(TEST_LIBRARIES obj_common obj_${EQUATION} obj_${EQUATION}_dependent)\nset(TEST_TARGET ryujin)\n\nif(TARGET obj_${EQUATION})\n  deal_ii_pickup_tests()\nendif()\n"
  },
  {
    "path": "tests/euler_barotropic/equation_of_state_library.cc",
    "content": "#include <barotropic_equation_of_state_isentropic.h>\n#include <barotropic_equation_of_state_isothermal.h>\n#include <barotropic_equation_of_state_library.h>\n\n#include <deal.II/base/array_view.h>\n#include <deal.II/lac/vector.h>\n\n#include <algorithm>\n#include <iomanip>\n#include <iostream>\n\n/*\n * Test the barotropic EOS library:\n */\n\nusing namespace ryujin::BarotropicEquationOfStateLibrary;\nusing namespace ryujin;\nusing namespace dealii;\n\nvoid test(\n    const ryujin::BarotropicEquationOfStateLibrary::BarotropicEquationOfState\n        &eos)\n{\n  const auto print_array =\n      [](const std::string &name, const auto array, auto &ostream) {\n        ostream << name << \" =\";\n        for (const auto &it : array)\n          ostream << \" \" << it;\n        ostream << std::endl;\n      };\n\n  std::cout << std::setprecision(10);\n  std::cout << std::scientific;\n  std::cout << \"name = \" << eos.name() << std::endl;\n\n  {\n    const auto rho = 1.4;\n    const auto p = eos.pressure(rho);\n    const auto e = eos.specific_internal_energy(rho);\n    const auto c = eos.speed_of_sound(rho);\n\n    std::cout << \"input rho      = \" << rho << std::endl //\n              << \"check p        = \" << p << std::endl   //\n              << \"check e        = \" << e << std::endl   //\n              << \"check c        = \" << c << std::endl;\n  }\n\n  {\n    std::array<double, 5> rho{{1.4, 1.3, 1.2, 1.1, 1.0}};\n\n    std::array<double, 5> p;\n    std::ranges::transform(std::begin(rho),\n                           std::end(rho),\n                           std::begin(p),\n                           [&](double rho) { return eos.pressure(rho); });\n\n    std::array<double, 5> e;\n    std::ranges::transform(\n        std::begin(rho), std::end(rho), std::begin(e), [&](double rho) {\n          return eos.specific_internal_energy(rho);\n        });\n\n    std::array<double, 5> c;\n    std::ranges::transform(std::begin(rho),\n                           std::end(rho),\n                           std::begin(c),\n                           [&](double rho) { return eos.speed_of_sound(rho); });\n\n\n    print_array(\"input rho     \", rho, std::cout);\n    print_array(\"check p       \", p, std::cout);\n    print_array(\"check e       \", e, std::cout);\n    print_array(\"check c       \", c, std::cout);\n  }\n}\n\nint main()\n{\n  /* isothermal */\n\n  std::cout << \"\\nIsothermal with c=2\" << std::endl;\n  Isothermal isothermal(\"\");\n  test(isothermal);\n\n  /* isentropic */\n\n  std::cout << \"\\nIsentropic with k=1, gamma=1.4\" << std::endl;\n  Isentropic isentropic(\"\");\n  test(isentropic);\n\n  return 0;\n}\n"
  },
  {
    "path": "tests/euler_barotropic/equation_of_state_library.threads=2.output",
    "content": "\nIsothermal with c=2\nname = isothermal\ninput rho      = 1.4000000000e+00\ncheck p        = 5.6000000000e+00\ncheck e        = 1.3458889465e+00\ncheck c        = 2.0000000000e+00\ninput rho      = 1.4000000000e+00 1.3000000000e+00 1.2000000000e+00 1.1000000000e+00 1.0000000000e+00\ncheck p        = 5.6000000000e+00 5.2000000000e+00 4.8000000000e+00 4.4000000000e+00 4.0000000000e+00\ncheck e        = 1.3458889465e+00 1.0494570579e+00 7.2928622718e-01 3.8124071922e-01 0.0000000000e+00\ncheck c        = 2.0000000000e+00 2.0000000000e+00 2.0000000000e+00 2.0000000000e+00 2.0000000000e+00\n\nIsentropic with k=1, gamma=1.4\nname = isentropic\ninput rho      = 1.4000000000e+00\ncheck p        = 1.6016928982e+00\ncheck e        = 2.8601658896e+00\ncheck c        = 1.2655800639e+00\ninput rho      = 1.4000000000e+00 1.3000000000e+00 1.2000000000e+00 1.1000000000e+00 1.0000000000e+00\ncheck p        = 1.6016928982e+00 1.4438453989e+00 1.2907845083e+00 1.1427461301e+00 1.0000000000e+00\ncheck e        = 2.8601658896e+00 2.7766257671e+00 2.6891343923e+00 2.5971502956e+00 2.5000000000e+00\ncheck c        = 1.2655800639e+00 1.2469604763e+00 1.2271573899e+00 1.2059868016e+00 1.1832159566e+00\n"
  },
  {
    "path": "tests/euler_barotropic/hyperbolic_system.cc",
    "content": "#include <hyperbolic_system.h>\n#include <simd.h>\n\n#include <deal.II/base/vectorization.h>\n\n#include <iomanip>\n#include <iostream>\n\n/*\n * Test EOS independent functions:\n */\n\nusing namespace ryujin::EulerBarotropic;\nusing namespace ryujin;\nusing namespace dealii;\n\n\nstatic HyperbolicSystem hyperbolic_system;\n\n\ntemplate <int dim, typename Number>\nvoid test()\n{\n  std::cout << std::setprecision(10);\n  std::cout << std::scientific;\n\n  const auto view = hyperbolic_system.view<dim, Number>();\n\n  using View = HyperbolicSystemView<dim, Number>;\n  using state_type = typename View::state_type;\n\n  const auto from_1d_state =\n      [](const dealii::Tensor<1, 2, Number> &state_1d) -> state_type {\n    const auto &rho = state_1d[0];\n    const auto &u = state_1d[1];\n\n    state_type state;\n\n    state[0] = rho;\n    state[1] = rho * u;\n\n    return state;\n  };\n\n  dealii::Tensor<1, 2, Number> state_1d;\n  state_1d[0] = 1.;\n  state_1d[1] = 3.;\n  const auto U = from_1d_state(state_1d);\n\n  const Number rho = view.density(U);\n  const Number e = view.beos_specific_internal_energy(rho);\n  const Number p = view.beos_pressure(rho);\n  const Number a = view.beos_speed_of_sound(rho);\n  const Number E = view.total_energy(U, e);\n  const state_type dE = view.total_energy_derivative(U, e, p);\n\n  std::cout << \"dim = \" << dim << std::endl;\n  std::cout << \"density = \"                  //\n            << view.density(U)               //\n            << std::endl;                    //\n  std::cout << \"momentum = \"                 //\n            << view.momentum(U)              //\n            << std::endl;                    //\n  std::cout << \"specific internal energy = \" //\n            << e                             //\n            << std::endl;                    //\n  std::cout << \"total energy = \"             //\n            << E                             //\n            << std::endl;                    //\n  std::cout << \"total energy derivative = \"  //\n            << dE                            //\n            << std::endl;                    //\n  std::cout << \"pressure = \"                 //\n            << p                             //\n            << std::endl;                    //\n  std::cout << \"speed of sound = \"           //\n            << a                             //\n            << std::endl;                    //\n  std::cout << \"f = \"                        //\n            << view.f(U, p)                  //\n            << std::endl;                    //\n}\n\nint main()\n{\n  const auto set_eos = [&](const std::string &eos) {\n    std::stringstream parameters;\n    parameters << \"subsection HyperbolicSystem\\n\"\n               << \"set barotropic equation of state = \" << eos << \"\\n\"\n               << \"end\\n\"\n               << std::endl;\n    ParameterAcceptor::initialize(parameters);\n  };\n\n  set_eos(\"isentropic\");\n  std::cout << \"\\ndouble:\\n\" << std::endl;\n  test<1, double>();\n  test<2, double>();\n  test<3, double>();\n  std::cout << \"\\nfloat:\\n\" << std::endl;\n  test<1, float>();\n  test<2, float>();\n  test<3, float>();\n\n  set_eos(\"isothermal\");\n  std::cout << \"\\ndouble:\\n\" << std::endl;\n  test<1, double>();\n  test<2, double>();\n  test<3, double>();\n  std::cout << \"\\nfloat:\\n\" << std::endl;\n  test<1, float>();\n  test<2, float>();\n  test<3, float>();\n\n  set_eos(\"function\");\n  std::cout << \"\\ndouble:\\n\" << std::endl;\n  test<1, double>();\n  test<2, double>();\n  test<3, double>();\n  std::cout << \"\\nfloat:\\n\" << std::endl;\n  test<1, float>();\n  test<2, float>();\n  test<3, float>();\n\n  return 0;\n}\n"
  },
  {
    "path": "tests/euler_barotropic/hyperbolic_system.threads=2.output",
    "content": "\ndouble:\n\ndim = 1\ndensity = 1.0000000000e+00\nmomentum = 3.0000000000e+00\nspecific internal energy = 2.5000000000e+00\ntotal energy = 7.0000000000e+00\ntotal energy derivative = -1.0000000000e+00 3.0000000000e+00\npressure = 1.0000000000e+00\nspeed of sound = 1.1832159566e+00\nf = 3.0000000000e+00 1.0000000000e+01\ndim = 2\ndensity = 1.0000000000e+00\nmomentum = 3.0000000000e+00 0.0000000000e+00\nspecific internal energy = 2.5000000000e+00\ntotal energy = 7.0000000000e+00\ntotal energy derivative = -1.0000000000e+00 3.0000000000e+00 0.0000000000e+00\npressure = 1.0000000000e+00\nspeed of sound = 1.1832159566e+00\nf = 3.0000000000e+00 0.0000000000e+00 1.0000000000e+01 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\ndim = 3\ndensity = 1.0000000000e+00\nmomentum = 3.0000000000e+00 0.0000000000e+00 0.0000000000e+00\nspecific internal energy = 2.5000000000e+00\ntotal energy = 7.0000000000e+00\ntotal energy derivative = -1.0000000000e+00 3.0000000000e+00 0.0000000000e+00 0.0000000000e+00\npressure = 1.0000000000e+00\nspeed of sound = 1.1832159566e+00\nf = 3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\n\nfloat:\n\ndim = 1\ndensity = 1.0000000000e+00\nmomentum = 3.0000000000e+00\nspecific internal energy = 2.5000000000e+00\ntotal energy = 7.0000000000e+00\ntotal energy derivative = -1.0000000000e+00 3.0000000000e+00\npressure = 1.0000000000e+00\nspeed of sound = 1.1832159758e+00\nf = 3.0000000000e+00 1.0000000000e+01\ndim = 2\ndensity = 1.0000000000e+00\nmomentum = 3.0000000000e+00 0.0000000000e+00\nspecific internal energy = 2.5000000000e+00\ntotal energy = 7.0000000000e+00\ntotal energy derivative = -1.0000000000e+00 3.0000000000e+00 0.0000000000e+00\npressure = 1.0000000000e+00\nspeed of sound = 1.1832159758e+00\nf = 3.0000000000e+00 0.0000000000e+00 1.0000000000e+01 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\ndim = 3\ndensity = 1.0000000000e+00\nmomentum = 3.0000000000e+00 0.0000000000e+00 0.0000000000e+00\nspecific internal energy = 2.5000000000e+00\ntotal energy = 7.0000000000e+00\ntotal energy derivative = -1.0000000000e+00 3.0000000000e+00 0.0000000000e+00 0.0000000000e+00\npressure = 1.0000000000e+00\nspeed of sound = 1.1832159758e+00\nf = 3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\n\ndouble:\n\ndim = 1\ndensity = 1.0000000000e+00\nmomentum = 3.0000000000e+00\nspecific internal energy = 0.0000000000e+00\ntotal energy = 4.5000000000e+00\ntotal energy derivative = -5.0000000000e-01 3.0000000000e+00\npressure = 4.0000000000e+00\nspeed of sound = 2.0000000000e+00\nf = 3.0000000000e+00 1.3000000000e+01\ndim = 2\ndensity = 1.0000000000e+00\nmomentum = 3.0000000000e+00 0.0000000000e+00\nspecific internal energy = 0.0000000000e+00\ntotal energy = 4.5000000000e+00\ntotal energy derivative = -5.0000000000e-01 3.0000000000e+00 0.0000000000e+00\npressure = 4.0000000000e+00\nspeed of sound = 2.0000000000e+00\nf = 3.0000000000e+00 0.0000000000e+00 1.3000000000e+01 0.0000000000e+00 0.0000000000e+00 4.0000000000e+00\ndim = 3\ndensity = 1.0000000000e+00\nmomentum = 3.0000000000e+00 0.0000000000e+00 0.0000000000e+00\nspecific internal energy = 0.0000000000e+00\ntotal energy = 4.5000000000e+00\ntotal energy derivative = -5.0000000000e-01 3.0000000000e+00 0.0000000000e+00 0.0000000000e+00\npressure = 4.0000000000e+00\nspeed of sound = 2.0000000000e+00\nf = 3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3000000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 4.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 4.0000000000e+00\n\nfloat:\n\ndim = 1\ndensity = 1.0000000000e+00\nmomentum = 3.0000000000e+00\nspecific internal energy = 0.0000000000e+00\ntotal energy = 4.5000000000e+00\ntotal energy derivative = -5.0000000000e-01 3.0000000000e+00\npressure = 4.0000000000e+00\nspeed of sound = 2.0000000000e+00\nf = 3.0000000000e+00 1.3000000000e+01\ndim = 2\ndensity = 1.0000000000e+00\nmomentum = 3.0000000000e+00 0.0000000000e+00\nspecific internal energy = 0.0000000000e+00\ntotal energy = 4.5000000000e+00\ntotal energy derivative = -5.0000000000e-01 3.0000000000e+00 0.0000000000e+00\npressure = 4.0000000000e+00\nspeed of sound = 2.0000000000e+00\nf = 3.0000000000e+00 0.0000000000e+00 1.3000000000e+01 0.0000000000e+00 0.0000000000e+00 4.0000000000e+00\ndim = 3\ndensity = 1.0000000000e+00\nmomentum = 3.0000000000e+00 0.0000000000e+00 0.0000000000e+00\nspecific internal energy = 0.0000000000e+00\ntotal energy = 4.5000000000e+00\ntotal energy derivative = -5.0000000000e-01 3.0000000000e+00 0.0000000000e+00 0.0000000000e+00\npressure = 4.0000000000e+00\nspeed of sound = 2.0000000000e+00\nf = 3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3000000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 4.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 4.0000000000e+00\n\ndouble:\n\ndim = 1\ndensity = 1.0000000000e+00\nmomentum = 3.0000000000e+00\nspecific internal energy = 0.0000000000e+00\ntotal energy = 4.5000000000e+00\ntotal energy derivative = -5.0000000000e-01 3.0000000000e+00\npressure = 4.0000000000e+00\nspeed of sound = 2.0000000000e+00\nf = 3.0000000000e+00 1.3000000000e+01\ndim = 2\ndensity = 1.0000000000e+00\nmomentum = 3.0000000000e+00 0.0000000000e+00\nspecific internal energy = 0.0000000000e+00\ntotal energy = 4.5000000000e+00\ntotal energy derivative = -5.0000000000e-01 3.0000000000e+00 0.0000000000e+00\npressure = 4.0000000000e+00\nspeed of sound = 2.0000000000e+00\nf = 3.0000000000e+00 0.0000000000e+00 1.3000000000e+01 0.0000000000e+00 0.0000000000e+00 4.0000000000e+00\ndim = 3\ndensity = 1.0000000000e+00\nmomentum = 3.0000000000e+00 0.0000000000e+00 0.0000000000e+00\nspecific internal energy = 0.0000000000e+00\ntotal energy = 4.5000000000e+00\ntotal energy derivative = -5.0000000000e-01 3.0000000000e+00 0.0000000000e+00 0.0000000000e+00\npressure = 4.0000000000e+00\nspeed of sound = 2.0000000000e+00\nf = 3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3000000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 4.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 4.0000000000e+00\n\nfloat:\n\ndim = 1\ndensity = 1.0000000000e+00\nmomentum = 3.0000000000e+00\nspecific internal energy = 0.0000000000e+00\ntotal energy = 4.5000000000e+00\ntotal energy derivative = -5.0000000000e-01 3.0000000000e+00\npressure = 4.0000000000e+00\nspeed of sound = 2.0000000000e+00\nf = 3.0000000000e+00 1.3000000000e+01\ndim = 2\ndensity = 1.0000000000e+00\nmomentum = 3.0000000000e+00 0.0000000000e+00\nspecific internal energy = 0.0000000000e+00\ntotal energy = 4.5000000000e+00\ntotal energy derivative = -5.0000000000e-01 3.0000000000e+00 0.0000000000e+00\npressure = 4.0000000000e+00\nspeed of sound = 2.0000000000e+00\nf = 3.0000000000e+00 0.0000000000e+00 1.3000000000e+01 0.0000000000e+00 0.0000000000e+00 4.0000000000e+00\ndim = 3\ndensity = 1.0000000000e+00\nmomentum = 3.0000000000e+00 0.0000000000e+00 0.0000000000e+00\nspecific internal energy = 0.0000000000e+00\ntotal energy = 4.5000000000e+00\ntotal energy derivative = -5.0000000000e-01 3.0000000000e+00 0.0000000000e+00 0.0000000000e+00\npressure = 4.0000000000e+00\nspeed of sound = 2.0000000000e+00\nf = 3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3000000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 4.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 4.0000000000e+00\n"
  },
  {
    "path": "tests/euler_barotropic/hyperbolic_system.threads=2.output.gcc-13.3-avx2",
    "content": "\ndouble:\n\ninterpolatory covolume: 0.0000000000e+00\ndim = 1\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 2.8089484437e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 1.3600000000e+01 2.9400000000e+01\ninterpolatory covolume: 0.0000000000e+00\ndim = 2\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 0.0000000000e+00 2.8089484437e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 1.3600000000e+01 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 2.9400000000e+01 0.0000000000e+00\ninterpolatory covolume: 0.0000000000e+00\ndim = 3\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 0.0000000000e+00 0.0000000000e+00 2.8089484437e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3600000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 2.9400000000e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 0.0000000000e+00\ndim = 1\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656244040e+00 -8.4268432856e-01 2.8089478612e-01\nsurrogate_pressure = 1.0000003576e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 1.3599999428e+01 2.9400001526e+01\ninterpolatory covolume: 0.0000000000e+00\ndim = 2\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656244040e+00 -8.4268432856e-01 0.0000000000e+00 2.8089478612e-01\nsurrogate_pressure = 1.0000003576e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 1.3599999428e+01 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 2.9400001526e+01 0.0000000000e+00\ninterpolatory covolume: 0.0000000000e+00\ndim = 3\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656244040e+00 -8.4268432856e-01 0.0000000000e+00 0.0000000000e+00 2.8089478612e-01\nsurrogate_pressure = 1.0000003576e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 1.3599999428e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 2.9400001526e+01 0.0000000000e+00 0.0000000000e+00\n\ndouble:\n\ninterpolatory covolume: 0.0000000000e+00\ndim = 1\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819023037e-01\nharten entropy = 1.2938989975e+00\nharten_entropy_derivative = 2.0418242801e+00 -1.2046645839e+00 4.0155486130e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 1.8100000000e+01 3.1983333333e+01\ninterpolatory covolume: 0.0000000000e+00\ndim = 2\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819023037e-01\nharten entropy = 1.2938989975e+00\nharten_entropy_derivative = 2.0418242801e+00 -1.2046645839e+00 0.0000000000e+00 4.0155486130e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 1.8100000000e+01 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 3.1983333333e+01 0.0000000000e+00\ninterpolatory covolume: 0.0000000000e+00\ndim = 3\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819023037e-01\nharten entropy = 1.2938989975e+00\nharten_entropy_derivative = 2.0418242801e+00 -1.2046645839e+00 0.0000000000e+00 0.0000000000e+00 4.0155486130e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00 1.8100000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 3.1983333333e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 0.0000000000e+00\ndim = 1\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819026709e-01\nharten entropy = 1.2938990593e+00\nharten_entropy_derivative = 2.0418241024e+00 -1.2046644688e+00 4.0155482292e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 1.8099998474e+01 3.1983331680e+01\ninterpolatory covolume: 0.0000000000e+00\ndim = 2\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819026709e-01\nharten entropy = 1.2938990593e+00\nharten_entropy_derivative = 2.0418241024e+00 -1.2046644688e+00 0.0000000000e+00 4.0155482292e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 1.8099998474e+01 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 3.1983331680e+01 0.0000000000e+00\ninterpolatory covolume: 0.0000000000e+00\ndim = 3\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819026709e-01\nharten entropy = 1.2938990593e+00\nharten_entropy_derivative = 2.0418241024e+00 -1.2046644688e+00 0.0000000000e+00 0.0000000000e+00 4.0155482292e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00 1.8099998474e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 3.1983331680e+01 0.0000000000e+00 0.0000000000e+00\n\ndouble:\n\ninterpolatory covolume: 1.0000000000e-01\ndim = 1\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694680208e+00\nharten entropy = 1.6435317327e+00\nharten_entropy_derivative = 1.6899437583e+00 -8.2176586633e-01 2.7392195544e-01\nsurrogate_pressure = 1.1627906977e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 1.3762790698e+01 2.9888372093e+01\ninterpolatory covolume: 1.0000000000e-01\ndim = 2\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694680208e+00\nharten entropy = 1.6435317327e+00\nharten_entropy_derivative = 1.6899437583e+00 -8.2176586633e-01 0.0000000000e+00 2.7392195544e-01\nsurrogate_pressure = 1.1627906977e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 1.3762790698e+01 0.0000000000e+00 0.0000000000e+00 1.1627906977e+00 2.9888372093e+01 0.0000000000e+00\ninterpolatory covolume: 1.0000000000e-01\ndim = 3\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694680208e+00\nharten entropy = 1.6435317327e+00\nharten_entropy_derivative = 1.6899437583e+00 -8.2176586633e-01 0.0000000000e+00 0.0000000000e+00 2.7392195544e-01\nsurrogate_pressure = 1.1627906977e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3762790698e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.1627906977e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.1627906977e+00 2.9888372093e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 1.0000000149e-01\ndim = 1\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694685936e+00\nharten entropy = 1.6435320377e+00\nharten_entropy_derivative = 1.6899434328e+00 -8.2176560163e-01 2.7392187715e-01\nsurrogate_pressure = 1.1627911329e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 1.3762790680e+01 2.9888372421e+01\ninterpolatory covolume: 1.0000000149e-01\ndim = 2\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694685936e+00\nharten entropy = 1.6435320377e+00\nharten_entropy_derivative = 1.6899434328e+00 -8.2176560163e-01 0.0000000000e+00 2.7392187715e-01\nsurrogate_pressure = 1.1627911329e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 1.3762790680e+01 0.0000000000e+00 0.0000000000e+00 1.1627911329e+00 2.9888372421e+01 0.0000000000e+00\ninterpolatory covolume: 1.0000000149e-01\ndim = 3\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694685936e+00\nharten entropy = 1.6435320377e+00\nharten_entropy_derivative = 1.6899434328e+00 -8.2176560163e-01 0.0000000000e+00 0.0000000000e+00 2.7392187715e-01\nsurrogate_pressure = 1.1627911329e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 1.3762790680e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.1627911329e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.1627911329e+00 2.9888372421e+01 0.0000000000e+00 0.0000000000e+00\n\ndouble:\n\ninterpolatory covolume: 1.0000000000e-01\ndim = 1\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149520626e-01\nharten entropy = 1.2119903874e+00\nharten_entropy_derivative = 1.8661328096e+00 -1.1284048434e+00 3.7613494781e-01\nsurrogate_pressure = 1.2345679012e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 1.8334567901e+01 3.2687037037e+01\ninterpolatory covolume: 1.0000000000e-01\ndim = 2\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149520626e-01\nharten entropy = 1.2119903874e+00\nharten_entropy_derivative = 1.8661328096e+00 -1.1284048434e+00 0.0000000000e+00 3.7613494781e-01\nsurrogate_pressure = 1.2345679012e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 1.8334567901e+01 0.0000000000e+00 0.0000000000e+00 1.2345679012e+00 3.2687037037e+01 0.0000000000e+00\ninterpolatory covolume: 1.0000000000e-01\ndim = 3\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149520626e-01\nharten entropy = 1.2119903874e+00\nharten_entropy_derivative = 1.8661328096e+00 -1.1284048434e+00 0.0000000000e+00 0.0000000000e+00 3.7613494781e-01\nsurrogate_pressure = 1.2345679012e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00 1.8334567901e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.2345679012e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.2345679012e+00 3.2687037037e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 1.0000000149e-01\ndim = 1\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149522305e-01\nharten entropy = 1.2119904757e+00\nharten_entropy_derivative = 1.8661326170e+00 -1.1284047365e+00 3.7613490224e-01\nsurrogate_pressure = 1.2345678806e+00\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 1.8334568024e+01 3.2687034607e+01\ninterpolatory covolume: 1.0000000149e-01\ndim = 2\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149522305e-01\nharten entropy = 1.2119904757e+00\nharten_entropy_derivative = 1.8661326170e+00 -1.1284047365e+00 0.0000000000e+00 3.7613490224e-01\nsurrogate_pressure = 1.2345678806e+00\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 1.8334568024e+01 0.0000000000e+00 0.0000000000e+00 1.2345678806e+00 3.2687034607e+01 0.0000000000e+00\ninterpolatory covolume: 1.0000000149e-01\ndim = 3\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149522305e-01\nharten entropy = 1.2119904757e+00\nharten_entropy_derivative = 1.8661326170e+00 -1.1284047365e+00 0.0000000000e+00 0.0000000000e+00 3.7613490224e-01\nsurrogate_pressure = 1.2345678806e+00\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00 1.8334568024e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.2345678806e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.2345678806e+00 3.2687034607e+01 0.0000000000e+00 0.0000000000e+00\n\ndouble:\n\ninterpolatory covolume: 5.0000000000e-01\ndim = 1\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429354725e-01\nharten entropy = 1.3789501326e+00\nharten_entropy_derivative = 1.0615727211e+00 -6.8947506630e-01 2.2982502210e-01\nsurrogate_pressure = 3.3333333333e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 1.5933333333e+01 3.6400000000e+01\ninterpolatory covolume: 5.0000000000e-01\ndim = 2\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429354725e-01\nharten entropy = 1.3789501326e+00\nharten_entropy_derivative = 1.0615727211e+00 -6.8947506630e-01 0.0000000000e+00 2.2982502210e-01\nsurrogate_pressure = 3.3333333333e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 1.5933333333e+01 0.0000000000e+00 0.0000000000e+00 3.3333333333e+00 3.6400000000e+01 0.0000000000e+00\ninterpolatory covolume: 5.0000000000e-01\ndim = 3\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429354725e-01\nharten entropy = 1.3789501326e+00\nharten_entropy_derivative = 1.0615727211e+00 -6.8947506630e-01 0.0000000000e+00 0.0000000000e+00 2.2982502210e-01\nsurrogate_pressure = 3.3333333333e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 1.5933333333e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 3.3333333333e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 3.3333333333e+00 3.6400000000e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 5.0000000000e-01\ndim = 1\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429401636e-01\nharten entropy = 1.3789503574e+00\nharten_entropy_derivative = 1.0615724325e+00 -6.8947488070e-01 2.2982497513e-01\nsurrogate_pressure = 3.3333344460e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 1.5933334351e+01 3.6400005341e+01\ninterpolatory covolume: 5.0000000000e-01\ndim = 2\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429401636e-01\nharten entropy = 1.3789503574e+00\nharten_entropy_derivative = 1.0615724325e+00 -6.8947488070e-01 0.0000000000e+00 2.2982497513e-01\nsurrogate_pressure = 3.3333344460e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 1.5933334351e+01 0.0000000000e+00 0.0000000000e+00 3.3333344460e+00 3.6400005341e+01 0.0000000000e+00\ninterpolatory covolume: 5.0000000000e-01\ndim = 3\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429401636e-01\nharten entropy = 1.3789503574e+00\nharten_entropy_derivative = 1.0615724325e+00 -6.8947488070e-01 0.0000000000e+00 0.0000000000e+00 2.2982497513e-01\nsurrogate_pressure = 3.3333344460e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 1.5933334351e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 3.3333344460e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 3.3333344460e+00 3.6400005341e+01 0.0000000000e+00 0.0000000000e+00\n\ndouble:\n\ninterpolatory covolume: 5.0000000000e-01\ndim = 1\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141072431e-02\nharten entropy = 5.1066071232e-01\nharten_entropy_derivative = -7.7896611380e-01 -4.7544273216e-01 1.5848091072e-01\nsurrogate_pressure = 2.0000000000e+01\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 3.7100000000e+01 8.8983333333e+01\ninterpolatory covolume: 5.0000000000e-01\ndim = 2\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141072431e-02\nharten entropy = 5.1066071232e-01\nharten_entropy_derivative = -7.7896611380e-01 -4.7544273216e-01 0.0000000000e+00 1.5848091072e-01\nsurrogate_pressure = 2.0000000000e+01\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 3.7100000000e+01 0.0000000000e+00 0.0000000000e+00 2.0000000000e+01 8.8983333333e+01 0.0000000000e+00\ninterpolatory covolume: 5.0000000000e-01\ndim = 3\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141072431e-02\nharten entropy = 5.1066071232e-01\nharten_entropy_derivative = -7.7896611380e-01 -4.7544273216e-01 0.0000000000e+00 0.0000000000e+00 1.5848091072e-01\nsurrogate_pressure = 2.0000000000e+01\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00 3.7100000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 2.0000000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 2.0000000000e+01 8.8983333333e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 5.0000000000e-01\ndim = 1\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141100839e-02\nharten entropy = 5.1066082716e-01\nharten_entropy_derivative = -7.7896571159e-01 -4.7544255853e-01 1.5848085284e-01\nsurrogate_pressure = 1.9999996185e+01\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 3.7099994659e+01 8.8983314514e+01\ninterpolatory covolume: 5.0000000000e-01\ndim = 2\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141100839e-02\nharten entropy = 5.1066082716e-01\nharten_entropy_derivative = -7.7896571159e-01 -4.7544255853e-01 0.0000000000e+00 1.5848085284e-01\nsurrogate_pressure = 1.9999996185e+01\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 3.7099994659e+01 0.0000000000e+00 0.0000000000e+00 1.9999996185e+01 8.8983314514e+01 0.0000000000e+00\ninterpolatory covolume: 5.0000000000e-01\ndim = 3\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111111641e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141100839e-02\nharten entropy = 5.1066082716e-01\nharten_entropy_derivative = -7.7896571159e-01 -4.7544255853e-01 0.0000000000e+00 0.0000000000e+00 1.5848085284e-01\nsurrogate_pressure = 1.9999996185e+01\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00 3.7099994659e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.9999996185e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.9999996185e+01 8.8983314514e+01 0.0000000000e+00 0.0000000000e+00\n"
  },
  {
    "path": "tests/euler_barotropic/hyperbolic_system.threads=2.output.osx-m1",
    "content": "\ndouble:\n\ninterpolatory covolume: 0.0000000000e+00\ndim = 1\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 2.8089484437e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 1.3600000000e+01 2.9400000000e+01\ninterpolatory covolume: 0.0000000000e+00\ndim = 2\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 0.0000000000e+00 2.8089484437e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 1.3600000000e+01 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 2.9400000000e+01 0.0000000000e+00\ninterpolatory covolume: 0.0000000000e+00\ndim = 3\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608485265e+00\nharten entropy = 1.6853690662e+00\nharten_entropy_derivative = 1.7656247360e+00 -8.4268453310e-01 0.0000000000e+00 0.0000000000e+00 2.8089484437e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3600000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 2.9400000000e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 0.0000000000e+00\ndim = 1\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656244040e+00 -8.4268432856e-01 2.8089478612e-01\nsurrogate_pressure = 1.0000003576e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 1.3599999428e+01 2.9400001526e+01\ninterpolatory covolume: 0.0000000000e+00\ndim = 2\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656244040e+00 -8.4268432856e-01 0.0000000000e+00 2.8089478612e-01\nsurrogate_pressure = 1.0000003576e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 1.3599999428e+01 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 2.9400001526e+01 0.0000000000e+00\ninterpolatory covolume: 0.0000000000e+00\ndim = 3\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.5608491898e+00\nharten entropy = 1.6853692532e+00\nharten_entropy_derivative = 1.7656244040e+00 -8.4268432856e-01 0.0000000000e+00 0.0000000000e+00 2.8089478612e-01\nsurrogate_pressure = 1.0000003576e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 1.3599999428e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000003576e+00 2.9400001526e+01 0.0000000000e+00 0.0000000000e+00\n\ndouble:\n\ninterpolatory covolume: 0.0000000000e+00\ndim = 1\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819023037e-01\nharten entropy = 1.2938989975e+00\nharten_entropy_derivative = 2.0418242801e+00 -1.2046645839e+00 4.0155486130e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 1.8100000000e+01 3.1983333333e+01\ninterpolatory covolume: 0.0000000000e+00\ndim = 2\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819023037e-01\nharten entropy = 1.2938989975e+00\nharten_entropy_derivative = 2.0418242801e+00 -1.2046645839e+00 0.0000000000e+00 4.0155486130e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 1.8100000000e+01 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 3.1983333333e+01 0.0000000000e+00\ninterpolatory covolume: 0.0000000000e+00\ndim = 3\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819023037e-01\nharten entropy = 1.2938989975e+00\nharten_entropy_derivative = 2.0418242801e+00 -1.2046645839e+00 0.0000000000e+00 0.0000000000e+00 4.0155486130e-01\nsurrogate_pressure = 1.0000000000e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00 1.8100000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 3.1983333333e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 0.0000000000e+00\ndim = 1\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111106873e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819014788e-01\nharten entropy = 1.2938989401e+00\nharten_entropy_derivative = 2.0418245792e+00 -1.2046647072e+00 4.0155491233e-01\nsurrogate_pressure = 9.9999958277e-01\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 1.8099998474e+01 3.1983331680e+01\ninterpolatory covolume: 0.0000000000e+00\ndim = 2\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111106873e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819014788e-01\nharten entropy = 1.2938989401e+00\nharten_entropy_derivative = 2.0418245792e+00 -1.2046647072e+00 0.0000000000e+00 4.0155491233e-01\nsurrogate_pressure = 9.9999958277e-01\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 1.8099998474e+01 0.0000000000e+00 0.0000000000e+00 9.9999958277e-01 3.1983331680e+01 0.0000000000e+00\ninterpolatory covolume: 0.0000000000e+00\ndim = 3\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111106873e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 3.2819014788e-01\nharten entropy = 1.2938989401e+00\nharten_entropy_derivative = 2.0418245792e+00 -1.2046647072e+00 0.0000000000e+00 0.0000000000e+00 4.0155491233e-01\nsurrogate_pressure = 9.9999958277e-01\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00 1.8099998474e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 9.9999958277e-01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 9.9999958277e-01 3.1983331680e+01 0.0000000000e+00 0.0000000000e+00\n\ndouble:\n\ninterpolatory covolume: 1.0000000000e-01\ndim = 1\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694680208e+00\nharten entropy = 1.6435317327e+00\nharten_entropy_derivative = 1.6899437583e+00 -8.2176586633e-01 2.7392195544e-01\nsurrogate_pressure = 1.1627906977e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 1.3762790698e+01 2.9888372093e+01\ninterpolatory covolume: 1.0000000000e-01\ndim = 2\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694680208e+00\nharten entropy = 1.6435317327e+00\nharten_entropy_derivative = 1.6899437583e+00 -8.2176586633e-01 0.0000000000e+00 2.7392195544e-01\nsurrogate_pressure = 1.1627906977e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 1.3762790698e+01 0.0000000000e+00 0.0000000000e+00 1.1627906977e+00 2.9888372093e+01 0.0000000000e+00\ninterpolatory covolume: 1.0000000000e-01\ndim = 3\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694680208e+00\nharten entropy = 1.6435317327e+00\nharten_entropy_derivative = 1.6899437583e+00 -8.2176586633e-01 0.0000000000e+00 0.0000000000e+00 2.7392195544e-01\nsurrogate_pressure = 1.1627906977e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 1.3762790698e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.1627906977e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.1627906977e+00 2.9888372093e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 1.0000000149e-01\ndim = 1\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694684744e+00\nharten entropy = 1.6435319185e+00\nharten_entropy_derivative = 1.6899434328e+00 -8.2176560163e-01 2.7392187715e-01\nsurrogate_pressure = 1.1627911329e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 1.3762790680e+01 2.9888372421e+01\ninterpolatory covolume: 1.0000000149e-01\ndim = 2\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694684744e+00\nharten entropy = 1.6435319185e+00\nharten_entropy_derivative = 1.6899434328e+00 -8.2176560163e-01 0.0000000000e+00 2.7392187715e-01\nsurrogate_pressure = 1.1627911329e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 1.3762790680e+01 0.0000000000e+00 0.0000000000e+00 1.1627911329e+00 2.9888372421e+01 0.0000000000e+00\ninterpolatory covolume: 1.0000000149e-01\ndim = 3\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 1.4694684744e+00\nharten entropy = 1.6435319185e+00\nharten_entropy_derivative = 1.6899434328e+00 -8.2176560163e-01 0.0000000000e+00 0.0000000000e+00 2.7392187715e-01\nsurrogate_pressure = 1.1627911329e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 1.3762790680e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.1627911329e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.1627911329e+00 2.9888372421e+01 0.0000000000e+00 0.0000000000e+00\n\ndouble:\n\ninterpolatory covolume: 1.0000000000e-01\ndim = 1\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149520626e-01\nharten entropy = 1.2119903874e+00\nharten_entropy_derivative = 1.8661328096e+00 -1.1284048434e+00 3.7613494781e-01\nsurrogate_pressure = 1.2345679012e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 1.8334567901e+01 3.2687037037e+01\ninterpolatory covolume: 1.0000000000e-01\ndim = 2\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149520626e-01\nharten entropy = 1.2119903874e+00\nharten_entropy_derivative = 1.8661328096e+00 -1.1284048434e+00 0.0000000000e+00 3.7613494781e-01\nsurrogate_pressure = 1.2345679012e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 1.8334567901e+01 0.0000000000e+00 0.0000000000e+00 1.2345679012e+00 3.2687037037e+01 0.0000000000e+00\ninterpolatory covolume: 1.0000000000e-01\ndim = 3\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149520626e-01\nharten entropy = 1.2119903874e+00\nharten_entropy_derivative = 1.8661328096e+00 -1.1284048434e+00 0.0000000000e+00 0.0000000000e+00 3.7613494781e-01\nsurrogate_pressure = 1.2345679012e+00\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00 1.8334567901e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.2345679012e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.2345679012e+00 3.2687037037e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 1.0000000149e-01\ndim = 1\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111106873e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149513364e-01\nharten entropy = 1.2119903564e+00\nharten_entropy_derivative = 1.8661328554e+00 -1.1284048557e+00 3.7613496184e-01\nsurrogate_pressure = 1.2345674038e+00\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 1.8334566116e+01 3.2687034607e+01\ninterpolatory covolume: 1.0000000149e-01\ndim = 2\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111106873e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149513364e-01\nharten entropy = 1.2119903564e+00\nharten_entropy_derivative = 1.8661328554e+00 -1.1284048557e+00 0.0000000000e+00 3.7613496184e-01\nsurrogate_pressure = 1.2345674038e+00\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 1.8334566116e+01 0.0000000000e+00 0.0000000000e+00 1.2345674038e+00 3.2687034607e+01 0.0000000000e+00\ninterpolatory covolume: 1.0000000149e-01\ndim = 3\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111106873e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.7149513364e-01\nharten entropy = 1.2119903564e+00\nharten_entropy_derivative = 1.8661328554e+00 -1.1284048557e+00 0.0000000000e+00 0.0000000000e+00 3.7613496184e-01\nsurrogate_pressure = 1.2345674038e+00\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00 1.8334566116e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.2345674038e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.2345674038e+00 3.2687034607e+01 0.0000000000e+00 0.0000000000e+00\n\ndouble:\n\ninterpolatory covolume: 5.0000000000e-01\ndim = 1\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429354725e-01\nharten entropy = 1.3789501326e+00\nharten_entropy_derivative = 1.0615727211e+00 -6.8947506630e-01 2.2982502210e-01\nsurrogate_pressure = 3.3333333333e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 1.5933333333e+01 3.6400000000e+01\ninterpolatory covolume: 5.0000000000e-01\ndim = 2\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429354725e-01\nharten entropy = 1.3789501326e+00\nharten_entropy_derivative = 1.0615727211e+00 -6.8947506630e-01 0.0000000000e+00 2.2982502210e-01\nsurrogate_pressure = 3.3333333333e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 1.5933333333e+01 0.0000000000e+00 0.0000000000e+00 3.3333333333e+00 3.6400000000e+01 0.0000000000e+00\ninterpolatory covolume: 5.0000000000e-01\ndim = 3\ndensity = 1.4000000000e+00\nmomentum = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000000000e+00\ninternal_energy = 2.5000000000e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429354725e-01\nharten entropy = 1.3789501326e+00\nharten_entropy_derivative = 1.0615727211e+00 -6.8947506630e-01 0.0000000000e+00 0.0000000000e+00 2.2982502210e-01\nsurrogate_pressure = 3.3333333333e+00\nsurrogate_gamma = 1.4000000000e+00\nf = 4.2000000000e+00 0.0000000000e+00 0.0000000000e+00 1.5933333333e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 3.3333333333e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 3.3333333333e+00 3.6400000000e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 5.0000000000e-01\ndim = 1\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429401636e-01\nharten entropy = 1.3789503574e+00\nharten_entropy_derivative = 1.0615724325e+00 -6.8947488070e-01 2.2982497513e-01\nsurrogate_pressure = 3.3333344460e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 1.5933334351e+01 3.6400005341e+01\ninterpolatory covolume: 5.0000000000e-01\ndim = 2\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429401636e-01\nharten entropy = 1.3789503574e+00\nharten_entropy_derivative = 1.0615724325e+00 -6.8947488070e-01 0.0000000000e+00 2.2982497513e-01\nsurrogate_pressure = 3.3333344460e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 1.5933334351e+01 0.0000000000e+00 0.0000000000e+00 3.3333344460e+00 3.6400005341e+01 0.0000000000e+00\ninterpolatory covolume: 5.0000000000e-01\ndim = 3\ndensity = 1.3999999762e+00\nmomentum = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 8.8000001907e+00\ninternal_energy = 2.5000009537e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 9.6429401636e-01\nharten entropy = 1.3789503574e+00\nharten_entropy_derivative = 1.0615724325e+00 -6.8947488070e-01 0.0000000000e+00 0.0000000000e+00 2.2982497513e-01\nsurrogate_pressure = 3.3333344460e+00\nsurrogate_gamma = 1.3999999762e+00\nf = 4.1999998093e+00 0.0000000000e+00 0.0000000000e+00 1.5933334351e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 3.3333344460e+00 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 3.3333344460e+00 3.6400005341e+01 0.0000000000e+00 0.0000000000e+00\n\ndouble:\n\ninterpolatory covolume: 5.0000000000e-01\ndim = 1\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141072431e-02\nharten entropy = 5.1066071232e-01\nharten_entropy_derivative = -7.7896611380e-01 -4.7544273216e-01 1.5848091072e-01\nsurrogate_pressure = 2.0000000000e+01\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 3.7100000000e+01 8.8983333333e+01\ninterpolatory covolume: 5.0000000000e-01\ndim = 2\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141072431e-02\nharten entropy = 5.1066071232e-01\nharten_entropy_derivative = -7.7896611380e-01 -4.7544273216e-01 0.0000000000e+00 1.5848091072e-01\nsurrogate_pressure = 2.0000000000e+01\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 3.7100000000e+01 0.0000000000e+00 0.0000000000e+00 2.0000000000e+01 8.8983333333e+01 0.0000000000e+00\ninterpolatory covolume: 5.0000000000e-01\ndim = 3\ndensity = 1.9000000000e+00\nmomentum = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611111111e+00\ninternal_energy = 1.1111111111e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141072431e-02\nharten entropy = 5.1066071232e-01\nharten_entropy_derivative = -7.7896611380e-01 -4.7544273216e-01 0.0000000000e+00 0.0000000000e+00 1.5848091072e-01\nsurrogate_pressure = 2.0000000000e+01\nsurrogate_gamma = 1.9000000000e+00\nf = 5.7000000000e+00 0.0000000000e+00 0.0000000000e+00 3.7100000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 2.0000000000e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 2.0000000000e+01 8.8983333333e+01 0.0000000000e+00 0.0000000000e+00\n\nfloat:\n\ninterpolatory covolume: 5.0000000000e-01\ndim = 1\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111106873e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141091526e-02\nharten entropy = 5.1066076756e-01\nharten_entropy_derivative = -7.7896547318e-01 -4.7544273734e-01 1.5848091245e-01\nsurrogate_pressure = 1.9999986649e+01\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 3.7099983215e+01 8.8983291626e+01\ninterpolatory covolume: 5.0000000000e-01\ndim = 2\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111106873e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141091526e-02\nharten entropy = 5.1066076756e-01\nharten_entropy_derivative = -7.7896547318e-01 -4.7544273734e-01 0.0000000000e+00 1.5848091245e-01\nsurrogate_pressure = 1.9999986649e+01\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 3.7099983215e+01 0.0000000000e+00 0.0000000000e+00 1.9999986649e+01 8.8983291626e+01 0.0000000000e+00\ninterpolatory covolume: 5.0000000000e-01\ndim = 3\ndensity = 1.8999999762e+00\nmomentum = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00\ntotal_energy = 9.6611108780e+00\ninternal_energy = 1.1111106873e+00\ninternal_energy_derivative = 4.5000000000e+00 -3.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00\nspecific_entropy = 2.2141091526e-02\nharten entropy = 5.1066076756e-01\nharten_entropy_derivative = -7.7896547318e-01 -4.7544273734e-01 0.0000000000e+00 0.0000000000e+00 1.5848091245e-01\nsurrogate_pressure = 1.9999986649e+01\nsurrogate_gamma = 1.8999999762e+00\nf = 5.6999998093e+00 0.0000000000e+00 0.0000000000e+00 3.7099983215e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.9999986649e+01 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 1.9999986649e+01 8.8983291626e+01 0.0000000000e+00 0.0000000000e+00\n"
  },
  {
    "path": "tests/euler_barotropic/limiter.cc",
    "content": "// force distinct symbols in test\n#define EulerBarotropic EulerBarotropicTest\n\n#include <hyperbolic_system.h>\n#include <multicomponent_vector.h>\n#include <simd.h>\n\n#include <limiter.h>\n\n#define DEBUG_EXPENSIVE_BOUNDS_CHECK\n#define DEBUG_OUTPUT\n#define DEBUG_OUTPUT_LIMITER\n#include <limiter.template.h>\n\nusing namespace ryujin::EulerBarotropic;\nusing namespace ryujin;\nusing namespace dealii;\n\nint main()\n{\n  constexpr int dim = 1;\n\n  HyperbolicSystem hyperbolic_system;\n  Limiter<dim, double>::Parameters limiter_parameters;\n\n  using state_type = HyperbolicSystemView<dim, double>::state_type;\n\n  using bounds_type = Limiter<dim, double>::Bounds;\n\n  static constexpr unsigned int n_precomputed_values =\n      HyperbolicSystemView<dim, double>::n_precomputed_values;\n\n  using precomputed_type =\n      Vectors::MultiComponentVector<double, n_precomputed_values>;\n  precomputed_type dummy;\n\n  Limiter<dim, double> limiter(hyperbolic_system, limiter_parameters, dummy);\n\n  const auto view = hyperbolic_system.template view<dim, double>();\n\n  const auto test =\n      [&](const state_type &U, const state_type &P, const bounds_type &bounds) {\n        std::cout << \"State: \" << U << \"\\nDensity: \" << view.density(U)\n                  << \"\\nBounds: \" << bounds[0] << \" \" << bounds[1] << std::endl;\n\n        const auto &[l, success] = limiter.limit(bounds, U, P);\n\n        std::cout << \"l: \" << l;\n        if (success)\n          std::cout << \"\\nSuccess!\";\n        else\n          std::cout << \"\\nFailure!\";\n        std::cout << std::endl;\n      };\n\n  std::cout << std::setprecision(16);\n  std::cout << std::scientific;\n\n  {\n    std::cout << \"\\n\\nChecking exceptional cases:\" << std::endl;\n\n    std::cout << \"\\nMinimum density violation:\" << std::endl;\n    auto U = state_type{{0.8, 1.4}};\n    auto P = state_type{{-0.1, 0.1}};\n    auto bounds = bounds_type{0.9, 1.1};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMinimum density violation (eps):\" << std::endl;\n    U = state_type{{0.9 - 1.0e-10, 1.4}};\n    P = state_type{{-1.0e-20, 0.1}};\n    bounds = bounds_type{0.9, 1.1};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMaximum density violation:\" << std::endl;\n    U = state_type{{1.2, 1.4}};\n    P = state_type{{0.1, 0.1}};\n    bounds = bounds_type{0.9, 1.1};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMaximum density violation (eps):\" << std::endl;\n    U = state_type{{1.1 + 1.0e-10, 1.4}};\n    P = state_type{{1.0e-20, 0.1}};\n    bounds = bounds_type{0.9, 1.1};\n    test(U, P, bounds);\n  }\n\n  {\n    std::cout << \"\\n\\nChecking individual limiter components:\" << std::endl;\n\n    std::cout << \"\\nMinimum density bound\" << std::endl;\n    auto U = state_type{{1.0, 1.4}};\n    auto P = state_type{{-0.2, 0.1}};\n    auto bounds = bounds_type{0.9, 1.1};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMinimum density bound (eps):\" << std::endl;\n    U = state_type{{0.9 + 1.0e-10, 1.4}};\n    P = state_type{{-5.0e-10, 0.1}};\n    bounds = bounds_type{0.9, 1.1};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMaximum density bound\" << std::endl;\n    U = state_type{{1.0, 1.4}};\n    P = state_type{{0.2, 0.1}};\n    bounds = bounds_type{0.9, 1.1};\n    test(U, P, bounds);\n\n    std::cout << \"\\nMaximum density bound (eps):\" << std::endl;\n    U = state_type{{1.1 - 1.0e-10, 1.4}};\n    P = state_type{{5.0e-10, 0.1}};\n    bounds = bounds_type{0.9, 1.1};\n    test(U, P, bounds);\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "tests/euler_barotropic/limiter.threads=2.output",
    "content": "\n\nChecking exceptional cases:\n\nMinimum density violation:\nState: 8.0000000000000004e-01 1.3999999999999999e+00\nDensity: 8.0000000000000004e-01\nBounds: 9.0000000000000002e-01 1.1000000000000001e+00\nBounds violation: low-order density (critical)!\n\t\trho min:         0.9000000000000000\n\t\trho min (delta): 0.1000000000000000\n\t\trho:             0.8000000000000000\n\t\trho max (delta): 0.0000000000000000\n\t\trho max:         1.1000000000000001\n\nBounds violation: high-order density!\n\t\trho min:         0.9000000000000000\n\t\trho min (delta): 0.1000000000000000\n\t\trho:             0.8000000000000000\n\t\trho max (delta): 0.0000000000000000\n\t\trho max:         1.1000000000000001\n\nl: 0.0000000000000000\nFailure!\n\nMinimum density violation (eps):\nState: 0.8999999999000000 1.3999999999999999\nDensity: 0.8999999999000000\nBounds: 0.9000000000000000 1.1000000000000001\nBounds violation: low-order density (critical)!\n\t\trho min:         0.9000000000000000\n\t\trho min (delta): 0.0000000001000000\n\t\trho:             0.8999999999000000\n\t\trho max (delta): 0.0000000000000000\n\t\trho max:         1.1000000000000001\n\nBounds violation: high-order density!\n\t\trho min:         0.9000000000000000\n\t\trho min (delta): 0.0000000001000000\n\t\trho:             0.8999999999000000\n\t\trho max (delta): 0.0000000000000000\n\t\trho max:         1.1000000000000001\n\nl: 0.0000000000000000\nFailure!\n\nMaximum density violation:\nState: 1.2000000000000000 1.3999999999999999\nDensity: 1.2000000000000000\nBounds: 0.9000000000000000 1.1000000000000001\nBounds violation: low-order density (critical)!\n\t\trho min:         0.9000000000000000\n\t\trho min (delta): 0.0000000000000000\n\t\trho:             1.2000000000000000\n\t\trho max (delta): 0.0999999999999999\n\t\trho max:         1.1000000000000001\n\nBounds violation: high-order density!\n\t\trho min:         0.9000000000000000\n\t\trho min (delta): 0.0000000000000000\n\t\trho:             1.2000000000000000\n\t\trho max (delta): 0.0999999999999999\n\t\trho max:         1.1000000000000001\n\nl: 0.0000000000000000\nFailure!\n\nMaximum density violation (eps):\nState: 1.1000000001000001 1.3999999999999999\nDensity: 1.1000000001000001\nBounds: 0.9000000000000000 1.1000000000000001\nBounds violation: low-order density (critical)!\n\t\trho min:         0.9000000000000000\n\t\trho min (delta): 0.0000000000000000\n\t\trho:             1.1000000001000001\n\t\trho max (delta): 0.0000000001000000\n\t\trho max:         1.1000000000000001\n\nBounds violation: high-order density!\n\t\trho min:         0.9000000000000000\n\t\trho min (delta): 0.0000000000000000\n\t\trho:             1.1000000001000001\n\t\trho max (delta): 0.0000000001000000\n\t\trho max:         1.1000000000000001\n\nl: 0.0000000000000000\nFailure!\n\n\nChecking individual limiter components:\n\nMinimum density bound\nState: 1.0000000000000000 1.3999999999999999\nDensity: 1.0000000000000000\nBounds: 0.9000000000000000 1.1000000000000001\nl: 0.4999999999999993\nSuccess!\n\nMinimum density bound (eps):\nState: 0.9000000001000000 1.3999999999999999\nDensity: 0.9000000001000000\nBounds: 0.9000000000000000 1.1000000000000001\nl: 0.1999999188484877\nSuccess!\n\nMaximum density bound\nState: 1.0000000000000000 1.3999999999999999\nDensity: 1.0000000000000000\nBounds: 0.9000000000000000 1.1000000000000001\nl: 0.4999999999999998\nSuccess!\n\nMaximum density bound (eps):\nState: 1.0999999999000001 1.3999999999999999\nDensity: 1.0999999999000001\nBounds: 0.9000000000000000 1.1000000000000001\nl: 0.1999999188484877\nSuccess!\n"
  },
  {
    "path": "tests/euler_barotropic/riemann_solver.cc",
    "content": "// force distinct symbols in test\n#define EulerBarotropic EulerBarotropicTest\n\n#include <hyperbolic_system.h>\n#include <multicomponent_vector.h>\n#define DEBUG_RIEMANN_SOLVER\n#include <riemann_solver.h>\n#include <riemann_solver.template.h>\n#include <simd.h>\n\nusing namespace ryujin::EulerBarotropic;\nusing namespace ryujin;\nusing namespace dealii;\n\nint main()\n{\n  constexpr int dim = 1;\n\n  HyperbolicSystem hyperbolic_system;\n  RiemannSolver<dim, double>::Parameters riemann_solver_parameters;\n\n  static constexpr unsigned int n_precomputed_values =\n      HyperbolicSystemView<dim, double>::n_precomputed_values;\n  using precomputed_type =\n      Vectors::MultiComponentVector<double, n_precomputed_values>;\n  precomputed_type dummy;\n\n  RiemannSolver<dim> riemann_solver(\n      hyperbolic_system, riemann_solver_parameters, dummy);\n\n  const auto view = hyperbolic_system.view<dim, double>();\n\n  using state_type = dealii::Tensor<1, 1 + dim, double>;\n\n  const auto riemann_data = [&](const state_type &state) {\n    const double rho = view.density(state);\n    const double m = view.momentum(state)[0];\n    const double u = m / rho;\n    const double a = view.beos_speed_of_sound(rho);\n\n    std::array<double, 2> result;\n    result[0] = u;\n    result[1] = a;\n\n    return result;\n  };\n\n  const auto test = [&](const state_type &U_i, const state_type &U_j) {\n    std::cout << std::endl;\n    std::cout << U_i[0] << \" \" << U_i[1] << std::endl;\n    std::cout << U_j[0] << \" \" << U_j[1] << std::endl;\n    const auto rd_i = riemann_data(U_i);\n    const auto rd_j = riemann_data(U_j);\n    const auto lambda_max = riemann_solver.compute(rd_i, rd_j);\n    std::cout << lambda_max << std::endl;\n  };\n\n  const auto set_eos = [&](const std::string &eos) {\n    std::stringstream parameters;\n    parameters << \"subsection HyperbolicSystem\\n\"\n               << \"set barotropic equation of state = \" << eos << \"\\n\"\n               << \"end\\n\"\n               << std::endl;\n    ParameterAcceptor::initialize(parameters);\n  };\n\n  std::cout << std::setprecision(16);\n  std::cout << std::scientific;\n\n  // TODO: implement actual test cases\n\n  state_type U_i;\n  U_i[0] = 1.;\n  U_i[1] = 0.;\n\n  state_type U_j;\n  U_j[0] = 1.;\n  U_j[1] = 0.;\n\n  set_eos(\"isentropic\");\n  test(U_i, U_j);\n\n  set_eos(\"isothermal\");\n  test(U_i, U_j);\n\n  set_eos(\"function\");\n  test(U_i, U_j);\n\n  return 0;\n}\n"
  },
  {
    "path": "tests/euler_barotropic/riemann_solver.threads=2.output",
    "content": "\n1.0000000000000000e+00 0.0000000000000000e+00\n1.0000000000000000e+00 0.0000000000000000e+00\nu_left: 0.0000000000000000e+00\na_left: 1.1832159566199232e+00\nu_right: 0.0000000000000000e+00\na_right: 1.1832159566199232e+00\n1.1832159566199232e+00\n\n1.0000000000000000e+00 0.0000000000000000e+00\n1.0000000000000000e+00 0.0000000000000000e+00\nu_left: 0.0000000000000000e+00\na_left: 2.0000000000000000e+00\nu_right: 0.0000000000000000e+00\na_right: 2.0000000000000000e+00\n2.0000000000000000e+00\n\n1.0000000000000000e+00 0.0000000000000000e+00\n1.0000000000000000e+00 0.0000000000000000e+00\nu_left: 0.0000000000000000e+00\na_left: 2.0000000000000000e+00\nu_right: 0.0000000000000000e+00\na_right: 2.0000000000000000e+00\n2.0000000000000000e+00\n"
  },
  {
    "path": "tests/euler_barotropic/verification-isentropic_vortex_beos_function-2d-ssprk33-l5.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-euler-l5\n\n  set enable compute error      = true\n\n  set final time                = 2.0\n\n  set timer granularity         = 2.0\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension                     = 2\n  set equation                      = euler barotropic\n  set barotropic equation of state  = function\n  subsection function\n    set specific internal energy = 2. * sqrt(rho)\n    set pressure                 = pow(rho, 1.5)\n    set speed of sound           = 1.5 * sqrt(rho)\n  end\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 5\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set beta        = 5\n    set gamma       = 1.5\n    set mach number = 1\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.2\n  set cfl max            = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = ssprk 33\nend\n"
  },
  {
    "path": "tests/euler_barotropic/verification-isentropic_vortex_beos_function-2d-ssprk33-l5.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler barotropic« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 1089\nt     = 2.006194930255409\nLinf  = 0.04574147826603776\nL1    = 0.003514556656534476\nL2    = 0.008296719483558377\n"
  },
  {
    "path": "tests/euler_barotropic/verification-isentropic_vortex_beos_function-2d-ssprk33-l6.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-euler-l5\n\n  set enable compute error      = true\n\n  set final time                = 2.0\n\n  set timer granularity         = 2.0\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension                     = 2\n  set equation                      = euler barotropic\n  set barotropic equation of state  = function\n  subsection function\n    set specific internal energy = 2. * sqrt(rho)\n    set pressure                 = pow(rho, 1.5)\n    set speed of sound           = 1.5 * sqrt(rho)\n  end\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set beta        = 5\n    set gamma       = 1.5\n    set mach number = 1\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.2\n  set cfl max            = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = ssprk 33\nend\n"
  },
  {
    "path": "tests/euler_barotropic/verification-isentropic_vortex_beos_function-2d-ssprk33-l6.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler barotropic« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 4225\nt     = 2.002959182863472\nLinf  = 0.004774242798156616\nL1    = 0.0004341065166045018\nL2    = 0.000987246133067977\n"
  },
  {
    "path": "tests/euler_barotropic/verification-isentropic_vortex_beos_isentropic-2d-erk33-l5.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-euler-l5\n\n  set enable compute error      = true\n\n  set final time                = 2.0\n\n  set timer granularity         = 2.0\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension                     = 2\n  set equation                      = euler barotropic\n  set barotropic equation of state  = isentropic\n  subsection isentropic\n    set k     = 1\n    set gamma = 1.4\n  end\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 5\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.2\n  set cfl max            = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler_barotropic/verification-isentropic_vortex_beos_isentropic-2d-erk33-l5.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler barotropic« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 1089\nt     = 2.014709457312591\nLinf  = 0.04416405070501599\nL1    = 0.003213199810474218\nL2    = 0.007721799410940361\n"
  },
  {
    "path": "tests/euler_barotropic/verification-isentropic_vortex_beos_isentropic-2d-erk33-l6.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-euler-l6\n\n  set enable compute error      = true\n\n  set final time                = 2.0\n\n  set timer granularity         = 2.0\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension                     = 2\n  set equation                      = euler barotropic\n  set barotropic equation of state  = isentropic\n  subsection isentropic\n    set k     = 1\n    set gamma = 1.4\n  end\nend\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min            = 0.2\n  set cfl max            = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler_barotropic/verification-isentropic_vortex_beos_isentropic-2d-erk33-l6.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler barotropic« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 4225\nt     = 2.001587996425303\nLinf  = 0.004201549514863441\nL1    = 0.0003735542681698675\nL2    = 0.0008567112436261347\n"
  },
  {
    "path": "tests/euler_barotropic/verification-zero_pressure-1d-erk33-l4.prm",
    "content": "subsection A - TimeLoop\n  set basename             = zero_pressure-erk33\n\n  set enable compute error = true\n  set error quantities     = rho, m\n\n  set enforce final time        = true\n\n  set final time           = 0.66666666666667\n  set timer granularity    = 0.66666666666667\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension                    = 1\n  set equation                     = euler barotropic\n  set barotropic equation of state = pressureless\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 4\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = 0\n    set position top right        = 1\n    set subdivisions x            = 25\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = leblanc\n\n  set direction     = 1\n  # for cG put the discontinuity right in the center of a cell\n  set position      = 0.326732673267\nend\n\n\nsubsection F - HyperbolicModule\n  subsection indicator\n    set evc factor = 0.\n  end\n\n  subsection limiter\n    set iterations            = 2\n    set relaxation factor     = 1\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.10\n  set cfl max               = 0.10\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/euler_barotropic/verification-zero_pressure-1d-erk33-l4.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler barotropic« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 401\nt     = 0.66666666666667\nLinf  = 1.574827071578124\nL1    = 1.427372349460922\nL2    = 1.440118469018574\n"
  },
  {
    "path": "tests/euler_poisson/CMakeLists.txt",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2022 - 2026 by the ryujin authors\n##\n\nset(EQUATION euler_poisson)\n\ninclude_directories(\n  ${CMAKE_BINARY_DIR}/source/\n  ${CMAKE_SOURCE_DIR}/source/${EQUATION}\n  ${CMAKE_SOURCE_DIR}/source/\n  )\n\nset(TEST_LIBRARIES obj_common obj_${EQUATION} obj_${EQUATION}_dependent)\nset(TEST_TARGET ryujin)\n\nif(TARGET obj_${EQUATION})\n  deal_ii_pickup_tests()\nendif()\n"
  },
  {
    "path": "tests/euler_poisson/verification-isentropic_vortex-2d-strang_erk33_cn-full_restart-l6-cg.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler poisson« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 4225\nt     = 2.005478357979373\nLinf  = 0.005327310122732077\nL1    = 0.0003912111915847398\nL2    = 0.0009279323378705062\n"
  },
  {
    "path": "tests/euler_poisson/verification-isentropic_vortex-2d-strang_erk33_cn-full_restart-l6-cg.prm",
    "content": "subsection A - TimeLoop\n  set basename                 = validation-euler_poisson-l5\n\n  set enable compute error     = true\n\n  set final time               = 2.0\n  set timer granularity        = 2.0\n\n  set terminal update interval = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler poisson\n\n  set alpha                        = 1\n  set electrostatic configuration  = function\n\n  subsection function\n    set time dependent   = true\n    set magnetic field z = 0.\n\n    ##\n    # rho_b     = -T^(1 / (gamma - 1))\n    # T         = 1 - (gamma - 1) / (2 * gamma) * f^2\n    # f         = beta / (2 * pi) * exp(0.5 * (1 - |r|^2))\n    # r         = (x,y) - (x_0,y_0) - Mt\n    # x_0 = y_0 = (-1, -1)\n    # M         = ( 1,  1) / sqrt(2)\n    # gamma     = 1.4\n    # beta      = 5\n    ##\n\n    set background density = -pow(1.-(1.4-1.)/(2 * 1.4) * pow(5. / (2. * 3.1415926535897932) * exp(0.5 * (1. - pow(x + 1. - 0.707106781188 * t, 2.) - pow(y + 1. - 0.707106781188 * t, 2.))), 2.), 1. / (1.4 - 1.))\n  end\nend\n\nsubsection C - Discretization\n  set finite element ansatz = cG Q1\n\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection G - ParabolicModule\n  set gauss law restart strategy = full restart\n\n  set multigrid - max iter          = 15\n  set multigrid - chebyshev max eig = 2\n  set multigrid - chebyshev range   = 8\n  set multigrid - chebyshev degree  = 3\n  set multigrid - chebyshev cg iter = 0\n  set multigrid - min level         = 0\n  set tolerance                     = 1e-12\n  set tolerance linfty norm         = false\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.2\n  set cfl max               = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang erk 33 cn\nend\n"
  },
  {
    "path": "tests/euler_poisson/verification-isentropic_vortex-2d-strang_erk33_cn-full_restart-l6.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler poisson« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 16384\nt     = 2.000605163614398\nLinf  = 0.0823001876118359\nL1    = 0.004106186514096906\nL2    = 0.01067632048526058\n"
  },
  {
    "path": "tests/euler_poisson/verification-isentropic_vortex-2d-strang_erk33_cn-full_restart-l6.prm",
    "content": "subsection A - TimeLoop\n  set basename                 = validation-euler_poisson-l5\n\n  set enable compute error     = true\n\n  set final time               = 2.0\n  set timer granularity        = 2.0\n\n  set terminal update interval = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler poisson\n\n  set alpha                        = 1\n  set electrostatic configuration  = function\n\n  subsection function\n    set time dependent   = true\n    set magnetic field z = 0.\n\n    ##\n    # rho_b     = -T^(1 / (gamma - 1))\n    # T         = 1 - (gamma - 1) / (2 * gamma) * f^2\n    # f         = beta / (2 * pi) * exp(0.5 * (1 - |r|^2))\n    # r         = (x,y) - (x_0,y_0) - Mt\n    # x_0 = y_0 = (-1, -1)\n    # M         = ( 1,  1) / sqrt(2)\n    # gamma     = 1.4\n    # beta      = 5\n    ##\n\n    set background density = -pow(1.-(1.4-1.)/(2 * 1.4) * pow(5. / (2. * 3.1415926535897932) * exp(0.5 * (1. - pow(x + 1. - 0.707106781188 * t, 2.) - pow(y + 1. - 0.707106781188 * t, 2.))), 2.), 1. / (1.4 - 1.))\n  end\nend\n\nsubsection C - Discretization\n  set finite element ansatz = dG Q1\n\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection G - ParabolicModule\n  set gauss law restart strategy = full restart\n\n  set multigrid - max iter          = 15\n  set multigrid - chebyshev max eig = 2\n  set multigrid - chebyshev range   = 8\n  set multigrid - chebyshev degree  = 3\n  set multigrid - chebyshev cg iter = 0\n  set multigrid - min level         = 0\n  set tolerance                     = 1e-12\n  set tolerance linfty norm         = false\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.2\n  set cfl max               = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang erk 33 cn\nend\n"
  },
  {
    "path": "tests/euler_poisson/verification-isentropic_vortex-2d-strang_erk33_cn-l6-cg.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler poisson« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 4225\nt     = 2.005478057664087\nLinf  = 0.00670781438489004\nL1    = 0.0009040816009143232\nL2    = 0.001486747955633116\n"
  },
  {
    "path": "tests/euler_poisson/verification-isentropic_vortex-2d-strang_erk33_cn-l6-cg.prm",
    "content": "subsection A - TimeLoop\n  set basename                 = validation-euler_poisson-l5\n\n  set enable compute error     = true\n\n  set final time               = 2.0\n  set timer granularity        = 2.0\n\n  set terminal update interval = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler poisson\n\n  set alpha                        = 1\n  set electrostatic configuration  = function\n\n  subsection function\n    set time dependent   = true\n    set magnetic field z = 0.\n\n    ##\n    # rho_b     = -T^(1 / (gamma - 1))\n    # T         = 1 - (gamma - 1) / (2 * gamma) * f^2\n    # f         = beta / (2 * pi) * exp(0.5 * (1 - |r|^2))\n    # r         = (x,y) - (x_0,y_0) - Mt\n    # x_0 = y_0 = (-1, -1)\n    # M         = ( 1,  1) / sqrt(2)\n    # gamma     = 1.4\n    # beta      = 5\n    ##\n\n    set background density = -pow(1.-(1.4-1.)/(2 * 1.4) * pow(5. / (2. * 3.1415926535897932) * exp(0.5 * (1. - pow(x + 1. - 0.707106781188 * t, 2.) - pow(y + 1. - 0.707106781188 * t, 2.))), 2.), 1. / (1.4 - 1.))\n  end\nend\n\nsubsection C - Discretization\n  set finite element ansatz = cG Q1\n\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection G - ParabolicModule\n  set gauss law restart strategy = no restart\n\n  set multigrid - max iter          = 15\n  set multigrid - chebyshev max eig = 2\n  set multigrid - chebyshev range   = 8\n  set multigrid - chebyshev degree  = 3\n  set multigrid - chebyshev cg iter = 0\n  set multigrid - min level         = 0\n  set tolerance                     = 1e-12\n  set tolerance linfty norm         = false\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.2\n  set cfl max               = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang erk 33 cn\nend\n"
  },
  {
    "path": "tests/euler_poisson/verification-isentropic_vortex-2d-strang_erk33_cn-l6.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler poisson« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 16384\nt     = 2.00064947603475\nLinf  = 0.08180492226385325\nL1    = 0.004288946197201713\nL2    = 0.01062317598668895\n"
  },
  {
    "path": "tests/euler_poisson/verification-isentropic_vortex-2d-strang_erk33_cn-l6.prm",
    "content": "subsection A - TimeLoop\n  set basename                 = validation-euler_poisson-l5\n\n  set enable compute error     = true\n\n  set final time               = 2.0\n  set timer granularity        = 2.0\n\n  set terminal update interval = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler poisson\n\n  set alpha                        = 1\n  set electrostatic configuration  = function\n\n  subsection function\n    set time dependent   = true\n    set magnetic field z = 0.\n\n    ##\n    # rho_b     = -T^(1 / (gamma - 1))\n    # T         = 1 - (gamma - 1) / (2 * gamma) * f^2\n    # f         = beta / (2 * pi) * exp(0.5 * (1 - |r|^2))\n    # r         = (x,y) - (x_0,y_0) - Mt\n    # x_0 = y_0 = (-1, -1)\n    # M         = ( 1,  1) / sqrt(2)\n    # gamma     = 1.4\n    # beta      = 5\n    ##\n\n    set background density = -pow(1.-(1.4-1.)/(2 * 1.4) * pow(5. / (2. * 3.1415926535897932) * exp(0.5 * (1. - pow(x + 1. - 0.707106781188 * t, 2.) - pow(y + 1. - 0.707106781188 * t, 2.))), 2.), 1. / (1.4 - 1.))\n  end\nend\n\nsubsection C - Discretization\n  set finite element ansatz = dG Q1\n\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection G - ParabolicModule\n  set gauss law restart strategy = no restart\n\n  set multigrid - max iter          = 15\n  set multigrid - chebyshev max eig = 2\n  set multigrid - chebyshev range   = 8\n  set multigrid - chebyshev degree  = 3\n  set multigrid - chebyshev cg iter = 0\n  set multigrid - min level         = 0\n  set tolerance                     = 1e-12\n  set tolerance linfty norm         = false\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.2\n  set cfl max               = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang erk 33 cn\nend\n"
  },
  {
    "path": "tests/euler_poisson/verification-isentropic_vortex-2d-strang_erk33_cn-static-l6-cg.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler poisson« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 4225\nt     = 2.005478361261119\nLinf  = 0.005315916737770971\nL1    = 0.0003914757708711799\nL2    = 0.0009275545215368286\n"
  },
  {
    "path": "tests/euler_poisson/verification-isentropic_vortex-2d-strang_erk33_cn-static-l6-cg.prm",
    "content": "subsection A - TimeLoop\n  set basename                 = validation-euler_poisson-l5\n\n  set enable compute error     = true\n\n  set final time               = 2.0\n  set timer granularity        = 2.0\n\n  set terminal update interval = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler poisson\n\n  set alpha                        = 1\n  set electrostatic configuration  = function\n\n  subsection function\n    set time dependent   = true\n    set magnetic field z = 0.\n\n    ##\n    # rho_b     = -T^(1 / (gamma - 1))\n    # T         = 1 - (gamma - 1) / (2 * gamma) * f^2\n    # f         = beta / (2 * pi) * exp(0.5 * (1 - |r|^2))\n    # r         = (x,y) - (x_0,y_0) - Mt\n    # x_0 = y_0 = (-1, -1)\n    # M         = ( 1,  1) / sqrt(2)\n    # gamma     = 1.4\n    # beta      = 5\n    ##\n\n    set background density = -pow(1.-(1.4-1.)/(2 * 1.4) * pow(5. / (2. * 3.1415926535897932) * exp(0.5 * (1. - pow(x + 1. - 0.707106781188 * t, 2.) - pow(y + 1. - 0.707106781188 * t, 2.))), 2.), 1. / (1.4 - 1.))\n  end\nend\n\nsubsection C - Discretization\n  set finite element ansatz = cG Q1\n\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection G - ParabolicModule\n  set gauss law restart strategy = static full restart\n\n  set multigrid - max iter          = 15\n  set multigrid - chebyshev max eig = 2\n  set multigrid - chebyshev range   = 8\n  set multigrid - chebyshev degree  = 3\n  set multigrid - chebyshev cg iter = 0\n  set multigrid - min level         = 0\n  set tolerance                     = 1e-12\n  set tolerance linfty norm         = false\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.2\n  set cfl max               = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang erk 33 cn\nend\n"
  },
  {
    "path": "tests/euler_poisson/verification-isentropic_vortex-2d-strang_erk33_cn-static-l6.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler poisson« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 16384\nt     = 2.00060379609223\nLinf  = 0.08230560713951192\nL1    = 0.004107298516732668\nL2    = 0.01067679992634559\n"
  },
  {
    "path": "tests/euler_poisson/verification-isentropic_vortex-2d-strang_erk33_cn-static-l6.prm",
    "content": "subsection A - TimeLoop\n  set basename                 = validation-euler_poisson-l5\n\n  set enable compute error     = true\n\n  set final time               = 2.0\n  set timer granularity        = 2.0\n\n  set terminal update interval = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler poisson\n\n  set alpha                        = 1\n  set electrostatic configuration  = function\n\n  subsection function\n    set time dependent   = true\n    set magnetic field z = 0.\n\n    ##\n    # rho_b     = -T^(1 / (gamma - 1))\n    # T         = 1 - (gamma - 1) / (2 * gamma) * f^2\n    # f         = beta / (2 * pi) * exp(0.5 * (1 - |r|^2))\n    # r         = (x,y) - (x_0,y_0) - Mt\n    # x_0 = y_0 = (-1, -1)\n    # M         = ( 1,  1) / sqrt(2)\n    # gamma     = 1.4\n    # beta      = 5\n    ##\n\n    set background density = -pow(1.-(1.4-1.)/(2 * 1.4) * pow(5. / (2. * 3.1415926535897932) * exp(0.5 * (1. - pow(x + 1. - 0.707106781188 * t, 2.) - pow(y + 1. - 0.707106781188 * t, 2.))), 2.), 1. / (1.4 - 1.))\n  end\nend\n\nsubsection C - Discretization\n  set finite element ansatz = dG Q1\n\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection G - ParabolicModule\n  set gauss law restart strategy = static full restart\n\n  set multigrid - max iter          = 15\n  set multigrid - chebyshev max eig = 2\n  set multigrid - chebyshev range   = 8\n  set multigrid - chebyshev degree  = 3\n  set multigrid - chebyshev cg iter = 0\n  set multigrid - min level         = 0\n  set tolerance                     = 1e-12\n  set tolerance linfty norm         = false\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.2\n  set cfl max               = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang erk 33 cn\nend\n"
  },
  {
    "path": "tests/euler_poisson_barotropic/CMakeLists.txt",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2022 - 2026 by the ryujin authors\n##\n\nset(EQUATION euler_poisson_barotropic)\n\ninclude_directories(\n  ${CMAKE_BINARY_DIR}/source/\n  ${CMAKE_SOURCE_DIR}/source/${EQUATION}\n  ${CMAKE_SOURCE_DIR}/source/\n  )\n\nset(TEST_LIBRARIES obj_common obj_${EQUATION} obj_${EQUATION}_dependent)\nset(TEST_TARGET ryujin)\n\nif(TARGET obj_${EQUATION})\n  deal_ii_pickup_tests()\nendif()\n"
  },
  {
    "path": "tests/euler_poisson_barotropic/verification-isentropic_vortex-2d-strang_erk33_cn-full_restart-l6-cg.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler poisson barotropic« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 4225\nt     = 2.001597259870147\nLinf  = 0.004098116791823933\nL1    = 0.0003620921915753326\nL2    = 0.0008417201597385104\n"
  },
  {
    "path": "tests/euler_poisson_barotropic/verification-isentropic_vortex-2d-strang_erk33_cn-full_restart-l6-cg.prm",
    "content": "subsection A - TimeLoop\n  set basename                 = validation-euler_poisson-l5\n\n  set enable compute error     = true\n\n  set final time               = 2.0\n  set timer granularity        = 2.0\n\n  set terminal update interval = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler poisson barotropic\n\n  set barotropic equation of state = isentropic\n  set alpha                        = 1\n  set electrostatic configuration  = function\n\n  subsection isentropic\n    set k     = 1.\n    set gamma = 1.4\n  end\n\n  subsection function\n    set time dependent   = true\n    set magnetic field z = 0.\n\n    ##\n    # rho_b     = -T^(1 / (gamma - 1))\n    # T         = 1 - (gamma - 1) / (2 * gamma) * f^2\n    # f         = beta / (2 * pi) * exp(0.5 * (1 - |r|^2))\n    # r         = (x,y) - (x_0,y_0) - Mt\n    # x_0 = y_0 = (-1, -1)\n    # M         = ( 1,  1) / sqrt(2)\n    # gamma     = 1.4\n    # beta      = 5\n    ##\n\n    set background density = -pow(1.-(1.4-1.)/(2 * 1.4) * pow(5. / (2. * 3.1415926535897932) * exp(0.5 * (1. - pow(x + 1. - 0.707106781188 * t, 2.) - pow(y + 1. - 0.707106781188 * t, 2.))), 2.), 1. / (1.4 - 1.))\n  end\nend\n\nsubsection C - Discretization\n  set finite element ansatz = cG Q1\n\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection G - ParabolicModule\n  set gauss law restart strategy = full restart\n\n  set multigrid - max iter          = 15\n  set multigrid - chebyshev max eig = 2\n  set multigrid - chebyshev range   = 8\n  set multigrid - chebyshev degree  = 3\n  set multigrid - chebyshev cg iter = 0\n  set multigrid - min level         = 0\n  set tolerance                     = 1e-12\n  set tolerance linfty norm         = false\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set gamma       = 1.4\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.2\n  set cfl max               = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang erk 33 cn\nend\n"
  },
  {
    "path": "tests/euler_poisson_barotropic/verification-isentropic_vortex-2d-strang_erk33_cn-full_restart-l6.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler poisson barotropic« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 16384\nt     = 2.001435481379366\nLinf  = 0.0656284634494223\nL1    = 0.003714143749484232\nL2    = 0.009184426109422222\n"
  },
  {
    "path": "tests/euler_poisson_barotropic/verification-isentropic_vortex-2d-strang_erk33_cn-full_restart-l6.prm",
    "content": "subsection A - TimeLoop\n  set basename                 = validation-euler_poisson-l5\n\n  set enable compute error     = true\n\n  set final time               = 2.0\n  set timer granularity        = 2.0\n\n  set terminal update interval = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler poisson barotropic\n\n  set barotropic equation of state = isentropic\n  set alpha                        = 1\n  set electrostatic configuration  = function\n\n  subsection isentropic\n    set k     = 1.\n    set gamma = 1.4\n  end\n\n  subsection function\n    set time dependent   = true\n    set magnetic field z = 0.\n\n    ##\n    # rho_b     = -T^(1 / (gamma - 1))\n    # T         = 1 - (gamma - 1) / (2 * gamma) * f^2\n    # f         = beta / (2 * pi) * exp(0.5 * (1 - |r|^2))\n    # r         = (x,y) - (x_0,y_0) - Mt\n    # x_0 = y_0 = (-1, -1)\n    # M         = ( 1,  1) / sqrt(2)\n    # gamma     = 1.4\n    # beta      = 5\n    ##\n\n    set background density = -pow(1.-(1.4-1.)/(2 * 1.4) * pow(5. / (2. * 3.1415926535897932) * exp(0.5 * (1. - pow(x + 1. - 0.707106781188 * t, 2.) - pow(y + 1. - 0.707106781188 * t, 2.))), 2.), 1. / (1.4 - 1.))\n  end\nend\n\nsubsection C - Discretization\n  set finite element ansatz = dG Q1\n\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection G - ParabolicModule\n  set gauss law restart strategy = full restart\n\n  set multigrid - max iter          = 15\n  set multigrid - chebyshev max eig = 2\n  set multigrid - chebyshev range   = 8\n  set multigrid - chebyshev degree  = 3\n  set multigrid - chebyshev cg iter = 0\n  set multigrid - min level         = 0\n  set tolerance                     = 1e-12\n  set tolerance linfty norm         = false\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set gamma       = 1.4\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.2\n  set cfl max               = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang erk 33 cn\nend\n"
  },
  {
    "path": "tests/euler_poisson_barotropic/verification-isentropic_vortex-2d-strang_erk33_cn-l6-cg.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler poisson barotropic« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 4225\nt     = 2.001660696590581\nLinf  = 0.005338856826093695\nL1    = 0.0006950378591318805\nL2    = 0.001173353381677097\n"
  },
  {
    "path": "tests/euler_poisson_barotropic/verification-isentropic_vortex-2d-strang_erk33_cn-l6-cg.prm",
    "content": "subsection A - TimeLoop\n  set basename                 = validation-euler_poisson-l5\n\n  set enable compute error     = true\n\n  set final time               = 2.0\n  set timer granularity        = 2.0\n\n  set terminal update interval = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler poisson barotropic\n\n  set barotropic equation of state = isentropic\n  set alpha                        = 1\n  set electrostatic configuration  = function\n\n  subsection isentropic\n    set k     = 1.\n    set gamma = 1.4\n  end\n\n  subsection function\n    set time dependent   = true\n    set magnetic field z = 0.\n\n    ##\n    # rho_b     = -T^(1 / (gamma - 1))\n    # T         = 1 - (gamma - 1) / (2 * gamma) * f^2\n    # f         = beta / (2 * pi) * exp(0.5 * (1 - |r|^2))\n    # r         = (x,y) - (x_0,y_0) - Mt\n    # x_0 = y_0 = (-1, -1)\n    # M         = ( 1,  1) / sqrt(2)\n    # gamma     = 1.4\n    # beta      = 5\n    ##\n\n    set background density = -pow(1.-(1.4-1.)/(2 * 1.4) * pow(5. / (2. * 3.1415926535897932) * exp(0.5 * (1. - pow(x + 1. - 0.707106781188 * t, 2.) - pow(y + 1. - 0.707106781188 * t, 2.))), 2.), 1. / (1.4 - 1.))\n  end\nend\n\nsubsection C - Discretization\n  set finite element ansatz = cG Q1\n\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection G - ParabolicModule\n  set gauss law restart strategy = no restart\n\n  set multigrid - max iter          = 15\n  set multigrid - chebyshev max eig = 2\n  set multigrid - chebyshev range   = 8\n  set multigrid - chebyshev degree  = 3\n  set multigrid - chebyshev cg iter = 0\n  set multigrid - min level         = 0\n  set tolerance                     = 1e-12\n  set tolerance linfty norm         = false\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set gamma       = 1.4\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.2\n  set cfl max               = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang erk 33 cn\nend\n"
  },
  {
    "path": "tests/euler_poisson_barotropic/verification-isentropic_vortex-2d-strang_erk33_cn-l6.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler poisson barotropic« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 16384\nt     = 2.001481663694795\nLinf  = 0.06533605252058426\nL1    = 0.003832538776907276\nL2    = 0.009154353391071958\n"
  },
  {
    "path": "tests/euler_poisson_barotropic/verification-isentropic_vortex-2d-strang_erk33_cn-l6.prm",
    "content": "subsection A - TimeLoop\n  set basename                 = validation-euler_poisson-l5\n\n  set enable compute error     = true\n\n  set final time               = 2.0\n  set timer granularity        = 2.0\n\n  set terminal update interval = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler poisson barotropic\n\n  set barotropic equation of state = isentropic\n  set alpha                        = 1\n  set electrostatic configuration  = function\n\n  subsection isentropic\n    set k     = 1.\n    set gamma = 1.4\n  end\n\n  subsection function\n    set time dependent   = true\n    set magnetic field z = 0.\n\n    ##\n    # rho_b     = -T^(1 / (gamma - 1))\n    # T         = 1 - (gamma - 1) / (2 * gamma) * f^2\n    # f         = beta / (2 * pi) * exp(0.5 * (1 - |r|^2))\n    # r         = (x,y) - (x_0,y_0) - Mt\n    # x_0 = y_0 = (-1, -1)\n    # M         = ( 1,  1) / sqrt(2)\n    # gamma     = 1.4\n    # beta      = 5\n    ##\n\n    set background density = -pow(1.-(1.4-1.)/(2 * 1.4) * pow(5. / (2. * 3.1415926535897932) * exp(0.5 * (1. - pow(x + 1. - 0.707106781188 * t, 2.) - pow(y + 1. - 0.707106781188 * t, 2.))), 2.), 1. / (1.4 - 1.))\n  end\nend\n\nsubsection C - Discretization\n  set finite element ansatz = dG Q1\n\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection G - ParabolicModule\n  set gauss law restart strategy = no restart\n\n  set multigrid - max iter          = 15\n  set multigrid - chebyshev max eig = 2\n  set multigrid - chebyshev range   = 8\n  set multigrid - chebyshev degree  = 3\n  set multigrid - chebyshev cg iter = 0\n  set multigrid - min level         = 0\n  set tolerance                     = 1e-12\n  set tolerance linfty norm         = false\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set gamma       = 1.4\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.2\n  set cfl max               = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang erk 33 cn\nend\n"
  },
  {
    "path": "tests/euler_poisson_barotropic/verification-isentropic_vortex-2d-strang_erk33_cn-static-l6-cg.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler poisson barotropic« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 4225\nt     = 2.001596262139737\nLinf  = 0.004089400289371982\nL1    = 0.0003623697105563334\nL2    = 0.0008414024532128203\n"
  },
  {
    "path": "tests/euler_poisson_barotropic/verification-isentropic_vortex-2d-strang_erk33_cn-static-l6-cg.prm",
    "content": "subsection A - TimeLoop\n  set basename                 = validation-euler_poisson-l5\n\n  set enable compute error     = true\n\n  set final time               = 2.0\n  set timer granularity        = 2.0\n\n  set terminal update interval = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler poisson barotropic\n\n  set barotropic equation of state = isentropic\n  set alpha                        = 1\n  set electrostatic configuration  = function\n\n  subsection isentropic\n    set k     = 1.\n    set gamma = 1.4\n  end\n\n  subsection function\n    set time dependent   = true\n    set magnetic field z = 0.\n\n    ##\n    # rho_b     = -T^(1 / (gamma - 1))\n    # T         = 1 - (gamma - 1) / (2 * gamma) * f^2\n    # f         = beta / (2 * pi) * exp(0.5 * (1 - |r|^2))\n    # r         = (x,y) - (x_0,y_0) - Mt\n    # x_0 = y_0 = (-1, -1)\n    # M         = ( 1,  1) / sqrt(2)\n    # gamma     = 1.4\n    # beta      = 5\n    ##\n\n    set background density = -pow(1.-(1.4-1.)/(2 * 1.4) * pow(5. / (2. * 3.1415926535897932) * exp(0.5 * (1. - pow(x + 1. - 0.707106781188 * t, 2.) - pow(y + 1. - 0.707106781188 * t, 2.))), 2.), 1. / (1.4 - 1.))\n  end\nend\n\nsubsection C - Discretization\n  set finite element ansatz = cG Q1\n\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection G - ParabolicModule\n  set gauss law restart strategy = static full restart\n\n  set multigrid - max iter          = 15\n  set multigrid - chebyshev max eig = 2\n  set multigrid - chebyshev range   = 8\n  set multigrid - chebyshev degree  = 3\n  set multigrid - chebyshev cg iter = 0\n  set multigrid - min level         = 0\n  set tolerance                     = 1e-12\n  set tolerance linfty norm         = false\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set gamma       = 1.4\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.2\n  set cfl max               = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang erk 33 cn\nend\n"
  },
  {
    "path": "tests/euler_poisson_barotropic/verification-isentropic_vortex-2d-strang_erk33_cn-static-l6.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »euler poisson barotropic« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 16384\nt     = 2.001433583966722\nLinf  = 0.06563462327921746\nL1    = 0.003715695437734766\nL2    = 0.009185162921432915\n"
  },
  {
    "path": "tests/euler_poisson_barotropic/verification-isentropic_vortex-2d-strang_erk33_cn-static-l6.prm",
    "content": "subsection A - TimeLoop\n  set basename                 = validation-euler_poisson-l5\n\n  set enable compute error     = true\n\n  set final time               = 2.0\n  set timer granularity        = 2.0\n\n  set terminal update interval = 0\nend\n\nsubsection B - Equation\n  set dimension = 2\n  set equation  = euler poisson barotropic\n\n  set barotropic equation of state = isentropic\n  set alpha                        = 1\n  set electrostatic configuration  = function\n\n  subsection isentropic\n    set k     = 1.\n    set gamma = 1.4\n  end\n\n  subsection function\n    set time dependent   = true\n    set magnetic field z = 0.\n\n    ##\n    # rho_b     = -T^(1 / (gamma - 1))\n    # T         = 1 - (gamma - 1) / (2 * gamma) * f^2\n    # f         = beta / (2 * pi) * exp(0.5 * (1 - |r|^2))\n    # r         = (x,y) - (x_0,y_0) - Mt\n    # x_0 = y_0 = (-1, -1)\n    # M         = ( 1,  1) / sqrt(2)\n    # gamma     = 1.4\n    # beta      = 5\n    ##\n\n    set background density = -pow(1.-(1.4-1.)/(2 * 1.4) * pow(5. / (2. * 3.1415926535897932) * exp(0.5 * (1. - pow(x + 1. - 0.707106781188 * t, 2.) - pow(y + 1. - 0.707106781188 * t, 2.))), 2.), 1. / (1.4 - 1.))\n  end\nend\n\nsubsection C - Discretization\n  set finite element ansatz = dG Q1\n\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -5, -5\n    set position top right        =  5,  5\n  end\nend\n\nsubsection G - ParabolicModule\n  set gauss law restart strategy = static full restart\n\n  set multigrid - max iter          = 15\n  set multigrid - chebyshev max eig = 2\n  set multigrid - chebyshev range   = 8\n  set multigrid - chebyshev degree  = 3\n  set multigrid - chebyshev cg iter = 0\n  set multigrid - min level         = 0\n  set tolerance                     = 1e-12\n  set tolerance linfty norm         = false\nend\n\nsubsection E - InitialValues\n  set configuration = isentropic vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection isentropic vortex\n    set gamma       = 1.4\n    set mach number = 1\n    set beta        = 5\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.2\n  set cfl max               = 0.2\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang erk 33 cn\nend\n"
  },
  {
    "path": "tests/navier_stokes/CMakeLists.txt",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2022 - 2024 by the ryujin authors\n##\n\nset(EQUATION navier_stokes)\n\ninclude_directories(\n  ${CMAKE_BINARY_DIR}/source/\n  ${CMAKE_SOURCE_DIR}/source/${EQUATION}\n  ${CMAKE_SOURCE_DIR}/source/\n  )\n\nset(TEST_LIBRARIES obj_common obj_${EQUATION} obj_${EQUATION}_dependent)\nset(TEST_TARGET ryujin)\n\nif(TARGET obj_${EQUATION})\n  deal_ii_pickup_tests()\nendif()\n"
  },
  {
    "path": "tests/navier_stokes/gmg_energy.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = test\n\n  set enable compute error      = true\n  set error quantities          = rho, m, E\n\n  set final time                = 2.0\n  set timer granularity         = 2.0\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = navier stokes\n  set gamma     = 1.4\n  set mu        = 0.01\n  set lambda    = 0\n  set kappa     = 1.866666666666666e-2\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 5\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = -0.25\n    set position top right        =  0.25\n  end\nend\n\n\nsubsection D - OfflineData\nend\n\n\nsubsection E - InitialValues\n  set configuration = becker solution\n  set direction     = 1\n  set position      = -0.125\n\n  subsection becker solution\n    set mu                      = 0.01\n    set velocity galilean frame = 0.125\n    set density left            = 1\n    set velocity left           = 1\n    set velocity right          = 0.259259259259\n  end\nend\n\n\nsubsection G - ParabolicModule\n  set tolerance             = 1e-16\n  set tolerance linfty norm = false\n\n  set multigrid velocity    = false\n  set multigrid energy      = true\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.30\n  set cfl max               = 0.30\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang erk 33 cn\nend\n"
  },
  {
    "path": "tests/navier_stokes/gmg_energy.threads=1.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »navier stokes« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 33\nt     = 2.004220028577319\nLinf  = 0.0409701982793451\nL1    = 0.01835826121512221\nL2    = 0.02060619780897954\n"
  },
  {
    "path": "tests/navier_stokes/gmg_velocity.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = test\n\n  set enable compute error      = true\n  set error quantities          = rho, m, E\n\n  set final time                = 2.0\n  set timer granularity         = 2.0\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = navier stokes\n  set gamma     = 1.4\n  set mu        = 0.01\n  set lambda    = 0\n  set kappa     = 1.866666666666666e-2\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 5\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = -0.25\n    set position top right        =  0.25\n  end\nend\n\n\nsubsection D - OfflineData\nend\n\n\nsubsection E - InitialValues\n  set configuration = becker solution\n  set direction     = 1\n  set position      = -0.125\n\n  subsection becker solution\n    set mu                      = 0.01\n    set velocity galilean frame = 0.125\n    set density left            = 1\n    set velocity left           = 1\n    set velocity right          = 0.259259259259\n  end\nend\n\n\nsubsection G - ParabolicModule\n  set tolerance             = 1e-16\n  set tolerance linfty norm = false\n\n  set multigrid velocity    = true\n  set multigrid energy      = true\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.30\n  set cfl max               = 0.30\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang erk 33 cn\nend\n"
  },
  {
    "path": "tests/navier_stokes/gmg_velocity.threads=1.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »navier stokes« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 33\nt     = 2.00422002857732\nLinf  = 0.04097019827896365\nL1    = 0.01835826121507542\nL2    = 0.0206061978089073\n"
  },
  {
    "path": "tests/navier_stokes/gmg_velocity_energy.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = test\n\n  set enable compute error      = true\n  set error quantities          = rho, m, E\n\n  set final time                = 2.0\n  set timer granularity         = 2.0\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = navier stokes\n  set gamma     = 1.4\n  set mu        = 0.01\n  set lambda    = 0\n  set kappa     = 1.866666666666666e-2\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 5\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = -0.25\n    set position top right        =  0.25\n  end\nend\n\n\nsubsection D - OfflineData\nend\n\n\nsubsection E - InitialValues\n  set configuration = becker solution\n  set direction     = 1\n  set position      = -0.125\n\n  subsection becker solution\n    set mu                      = 0.01\n    set velocity galilean frame = 0.125\n    set density left            = 1\n    set velocity left           = 1\n    set velocity right          = 0.259259259259\n  end\nend\n\n\nsubsection G - ParabolicModule\n  set tolerance             = 1e-16\n  set tolerance linfty norm = false\n\n  set multigrid velocity    = true\n  set multigrid energy      = false\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.30\n  set cfl max               = 0.30\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang erk 33 cn\nend\n"
  },
  {
    "path": "tests/navier_stokes/gmg_velocity_energy.threads=1.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »navier stokes« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 33\nt     = 2.00422002857732\nLinf  = 0.0409701982788602\nL1    = 0.01835826121505662\nL2    = 0.02060619780888481\n"
  },
  {
    "path": "tests/navier_stokes/test_restart.prm",
    "content": "#\n# Test that our admissibility test and restart logic works as intended. For\n# the following configuration we run into the issue that the first time\n# step with Crank Nicolson update for the parabolic part produces\n# inadmissible states. Check that we detect this and restart with a smaller\n# CFL number.\n#\n\nsubsection A - TimeLoop\n  set basename                      = test\n  set final time                    = 0.00000000000000000000000000001\n  set timer granularity             = 0.00000000000000000000000000001\n\n  set enable compute error          = true\n  set error quantities              = rho, m_1, m_2, E\n\n  set terminal update interval      = 0\nend\n\nsubsection B - Equation\n  set dimension                     = 2\n  set equation                      = navier stokes\n  set gamma                         = 1.6667\n  set reference density             = 1\n  set vacuum state relaxation small = 100\n  set vacuum state relaxation large = 10000\n  set mu                            = 0.001\n  set lambda                        = 0\n  set kappa                         = 0.0186667\nend\n\nsubsection C - Discretization\n  set finite element ansatz = cG Q1\n  set geometry              = disk\n  set mesh refinement       = 6\n  set mesh writeout         = false\n\n  subsection disk\n    set radius = 1.2\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = icf like\n  set direction     = 1, 0\n  set position      = 0, 0\n  set perturbation  = 0\n  subsection icf like\n    set primitive state inside  = 0.05, 0, 0.1\n    set primitive state outside = 1.00, 0, 0.1\n    set interface radius        = 1\n    set number of modes         = 12\n    set amplitude               = 0.02\n    set mach number             = 5\n    set shock radius            = 1.1\n  end\nend\n\nsubsection F - HyperbolicModule\n  subsection indicator\n    set evc factor         = 1\n  end\n  subsection limiter\n    set iterations            = 2\n    set newton tolerance      = 1e-10\n    set newton max iterations = 2\n    set relaxation factor     = 1\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.30\n  set cfl max               = 0.90\n  set cfl recovery strategy = bang bang control\n  set time stepping scheme  = strang erk 33 cn\nend\n"
  },
  {
    "path": "tests/navier_stokes/test_restart.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »navier stokes« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 49409\nt     = 0.001926033360247045\nLinf  = 1.574713013266967\nL1    = 0.1417401306265333\nL2    = 0.3409850523460464\n"
  },
  {
    "path": "tests/navier_stokes/verification-becker_solution-erk_33-l5-2d.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-becker-l5\n\n  set enable compute error      = true\n  set error quantities          = rho, m_1, E\n\n  set final time                = 2.0\n  set timer granularity         = 2.0\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension = 2\n  set equation = navier stokes\n  set gamma    = 1.4\n  set mu       = 0.01\n  set lambda   = 0\n  set kappa    = 1.866666666666666e-2\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 5\n\n  subsection rectangular domain\n    set boundary condition bottom = periodic\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = periodic\n\n    set position bottom left      = -0.25, -0.25\n    set position top right        =  0.25,  0.25\n  end\nend\n\n\nsubsection D - OfflineData\nend\n\n\nsubsection E - InitialValues\n  set configuration = becker solution\n  set direction     = 1,      0\n  set position      = -0.125, 0\n\n  subsection becker solution\n    set mu                      = 0.01\n    set velocity galilean frame = 0.125\n    set density left            = 1\n    set velocity left           = 1\n    set velocity right          = 0.259259259259\n  end\nend\n\n\nsubsection G - ParabolicModule\n  set tolerance             = 1e-16\n  set tolerance linfty norm = false\n\n  set multigrid velocity    = false\n  set multigrid energy      = false\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.30\n  set cfl max               = 0.30\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang erk 33 cn\nend\n"
  },
  {
    "path": "tests/navier_stokes/verification-becker_solution-erk_33-l5-2d.threads=1.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »navier stokes« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 1089\nt     = 2.00198656566548\nLinf  = 0.03852861151623161\nL1    = 0.0186757294224659\nL2    = 0.02057062514513074\n"
  },
  {
    "path": "tests/navier_stokes/verification-becker_solution-erk_33-l5.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-becker-l5\n\n  set enable compute error      = true\n  set error quantities          = rho, m, E\n\n  set final time                = 2.0\n  set timer granularity         = 2.0\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = navier stokes\n  set gamma     = 1.4\n  set mu        = 0.01\n  set lambda    = 0\n  set kappa     = 1.866666666666666e-2\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = -0.25\n    set position top right        =  0.25\n  end\nend\n\n\nsubsection D - OfflineData\nend\n\n\nsubsection E - InitialValues\n  set configuration = becker solution\n  set direction     = 1\n  set position      = -0.125\n\n  subsection becker solution\n    set mu                      = 0.01\n    set velocity galilean frame = 0.125\n    set density left            = 1\n    set velocity left           = 1\n    set velocity right          = 0.259259259259\n  end\nend\n\n\nsubsection G - ParabolicModule\n  set tolerance             = 1e-16\n  set tolerance linfty norm = false\n\n  set multigrid velocity    = false\n  set multigrid energy      = false\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.10\n  set cfl max               = 0.10\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang erk 33 cn\nend\n"
  },
  {
    "path": "tests/navier_stokes/verification-becker_solution-erk_33-l5.threads=1.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »navier stokes« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 65\nt     = 2.000401369791012\nLinf  = 0.01289896026381392\nL1    = 0.004946515988924531\nL2    = 0.006245316218548643\n"
  },
  {
    "path": "tests/navier_stokes/verification-becker_solution-erk_33-l6.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-becker-l6\n\n  set enable compute error      = true\n  set error quantities          = rho, m, E\n\n  set final time                = 2.0\n  set timer granularity         = 2.0\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = navier stokes\n  set gamma     = 1.4\n  set mu        = 0.01\n  set lambda    = 0\n  set kappa     = 1.866666666666666e-2\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 7\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = -0.25\n    set position top right        =  0.25\n  end\nend\n\n\nsubsection D - OfflineData\nend\n\n\nsubsection E - InitialValues\n  set configuration = becker solution\n  set direction     = 1\n  set position      = -0.125\n\n  subsection becker solution\n    set mu                      = 0.01\n    set velocity galilean frame = 0.125\n    set density left            = 1\n    set velocity left           = 1\n    set velocity right          = 0.259259259259\n  end\nend\n\n\nsubsection G - ParabolicModule\n  set tolerance             = 1e-16\n  set tolerance linfty norm = false\n\n  set multigrid velocity    = false\n  set multigrid energy      = false\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.10\n  set cfl max               = 0.10\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang erk 33 cn\nend\n"
  },
  {
    "path": "tests/navier_stokes/verification-becker_solution-erk_33-l6.threads=1.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »navier stokes« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 129\nt     = 2.000175338945207\nLinf  = 0.002861258858600509\nL1    = 0.0004061078173063114\nL2    = 0.0009444470187611757\n"
  },
  {
    "path": "tests/navier_stokes/verification-becker_solution-erk_33-l7.mpirun=4.threads=1.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »navier stokes« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 257\nt     = 2.00017487297486\nLinf  = 0.0007917775380829093\nL1    = 8.796256817787248e-05\nL2    = 0.0002338538850691109\n"
  },
  {
    "path": "tests/navier_stokes/verification-becker_solution-erk_33-l7.mpirun=8.threads=1.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »navier stokes« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 257\nt     = 2.00017487297486\nLinf  = 0.0007917775380874344\nL1    = 8.796256817738575e-05\nL2    = 0.0002338538850756005\n"
  },
  {
    "path": "tests/navier_stokes/verification-becker_solution-erk_33-l7.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-becker-l7\n\n  set enable compute error      = true\n  set error quantities          = rho, m, E\n\n  set final time                = 2.0\n  set timer granularity         = 2.0\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = navier stokes\n  set gamma     = 1.4\n  set mu        = 0.01\n  set lambda    = 0\n  set kappa     = 1.866666666666666e-2\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 8\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = -0.25\n    set position top right        =  0.25\n  end\nend\n\n\nsubsection D - OfflineData\nend\n\n\nsubsection E - InitialValues\n  set configuration = becker solution\n  set direction     = 1\n  set position      = -0.125\n\n  subsection becker solution\n    set mu                      = 0.01\n    set velocity galilean frame = 0.125\n    set density left            = 1\n    set velocity left           = 1\n    set velocity right          = 0.259259259259\n  end\nend\n\n\nsubsection G - ParabolicModule\n  set tolerance             = 1e-16\n  set tolerance linfty norm = false\n\n  set multigrid velocity    = false\n  set multigrid energy      = false\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.10\n  set cfl max               = 0.10\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang erk 33 cn\nend\n"
  },
  {
    "path": "tests/navier_stokes/verification-becker_solution-ssprk_33-l5-2d.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-becker-l5\n\n  set enable compute error      = true\n  set error quantities          = rho, m_1, E\n\n  set final time                = 2.0\n  set timer granularity         = 2.0\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension = 2\n  set equation = navier stokes\n  set gamma       = 1.4\n  set mu          = 0.01\n  set lambda      = 0\n  set kappa       = 1.866666666666666e-2\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 5\n\n  subsection rectangular domain\n    set boundary condition bottom = periodic\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = periodic\n\n    set position bottom left      = -0.25, -0.25\n    set position top right        =  0.25,  0.25\n  end\nend\n\n\nsubsection D - OfflineData\nend\n\n\nsubsection E - InitialValues\n  set configuration = becker solution\n  set direction     = 1,      0\n  set position      = -0.125, 0\n\n  subsection becker solution\n    set mu                      = 0.01\n    set velocity galilean frame = 0.125\n    set density left            = 1\n    set velocity left           = 1\n    set velocity right          = 0.259259259259\n  end\nend\n\n\nsubsection G - ParabolicModule\n  set tolerance             = 1e-16\n  set tolerance linfty norm = false\n\n  set multigrid velocity    = false\n  set multigrid energy      = false\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.30\n  set cfl max               = 0.30\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang ssprk 33 cn\nend\n"
  },
  {
    "path": "tests/navier_stokes/verification-becker_solution-ssprk_33-l5-2d.threads=1.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »navier stokes« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 1089\nt     = 2.001980562216737\nLinf  = 0.03975809034688541\nL1    = 0.0188860458707142\nL2    = 0.0208345816772768\n"
  },
  {
    "path": "tests/navier_stokes/verification-becker_solution-ssprk_33-l5.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-becker-l5\n\n  set enable compute error      = true\n  set error quantities          = rho, m, E\n\n  set final time                = 2.0\n  set timer granularity         = 2.0\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = navier stokes\n  set gamma     = 1.4\n  set mu        = 0.01\n  set lambda    = 0\n  set kappa     = 1.866666666666666e-2\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 6\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = -0.25\n    set position top right        =  0.25\n  end\nend\n\n\nsubsection D - OfflineData\nend\n\n\nsubsection E - InitialValues\n  set configuration = becker solution\n  set direction     = 1\n  set position      = -0.125\n\n  subsection becker solution\n    set mu                      = 0.01\n    set velocity galilean frame = 0.125\n    set density left            = 1\n    set velocity left           = 1\n    set velocity right          = 0.259259259259\n  end\nend\n\n\nsubsection G - ParabolicModule\n  set tolerance             = 1e-16\n  set tolerance linfty norm = false\n\n  set multigrid velocity    = false\n  set multigrid energy      = false\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.30\n  set cfl max               = 0.30\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang ssprk 33 cn\nend\n"
  },
  {
    "path": "tests/navier_stokes/verification-becker_solution-ssprk_33-l5.threads=1.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »navier stokes« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 65\nt     = 2.00050571184741\nLinf  = 0.01130318503117003\nL1    = 0.004179599526037562\nL2    = 0.005423097342316426\n"
  },
  {
    "path": "tests/navier_stokes/verification-becker_solution-ssprk_33-l6.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-becker-l6\n\n  set enable compute error      = true\n  set error quantities          = rho, m, E\n\n  set final time                = 2.0\n  set timer granularity         = 2.0\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = navier stokes\n  set gamma     = 1.4\n  set mu        = 0.01\n  set lambda    = 0\n  set kappa     = 1.866666666666666e-2\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 7\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = -0.25\n    set position top right        =  0.25\n  end\nend\n\n\nsubsection D - OfflineData\nend\n\n\nsubsection E - InitialValues\n  set configuration = becker solution\n  set direction     = 1\n  set position      = -0.125\n\n  subsection becker solution\n    set mu                      = 0.01\n    set velocity galilean frame = 0.125\n    set density left            = 1\n    set velocity left           = 1\n    set velocity right          = 0.259259259259\n  end\nend\n\n\nsubsection G - ParabolicModule\n  set tolerance             = 1e-16\n  set tolerance linfty norm = false\n\n  set multigrid velocity    = false\n  set multigrid energy      = false\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.30\n  set cfl max               = 0.30\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang ssprk 33 cn\nend\n"
  },
  {
    "path": "tests/navier_stokes/verification-becker_solution-ssprk_33-l6.threads=1.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »navier stokes« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 129\nt     = 2.000175245420349\nLinf  = 0.002861608339551725\nL1    = 0.0004069650584377154\nL2    = 0.0009451022811716695\n"
  },
  {
    "path": "tests/navier_stokes/verification-becker_solution-ssprk_33-l7.mpirun=4.threads=1.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »navier stokes« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 257\nt     = 2.000174875428266\nLinf  = 0.0007910891060395023\nL1    = 8.796698792094661e-05\nL2    = 0.000233846894285209\n"
  },
  {
    "path": "tests/navier_stokes/verification-becker_solution-ssprk_33-l7.mpirun=8.threads=1.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »navier stokes« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 257\nt     = 2.000174875428265\nLinf  = 0.000791089106008444\nL1    = 8.796698793039654e-05\nL2    = 0.0002338468942815917\n"
  },
  {
    "path": "tests/navier_stokes/verification-becker_solution-ssprk_33-l7.prm",
    "content": "subsection A - TimeLoop\n  set basename                  = validation-becker-l7\n\n  set enable compute error      = true\n  set error quantities          = rho, m, E\n\n  set final time                = 2.0\n  set timer granularity         = 2.0\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = navier stokes\n  set gamma     = 1.4\n  set mu        = 0.01\n  set lambda    = 0\n  set kappa     = 1.866666666666666e-2\nend\n\n\nsubsection C - Discretization\n  set geometry        = rectangular domain\n  set mesh refinement = 8\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = -0.25\n    set position top right        =  0.25\n  end\nend\n\n\nsubsection D - OfflineData\nend\n\n\nsubsection E - InitialValues\n  set configuration = becker solution\n  set direction     = 1\n  set position      = -0.125\n\n  subsection becker solution\n    set mu                      = 0.01\n    set velocity galilean frame = 0.125\n    set density left            = 1\n    set velocity left           = 1\n    set velocity right          = 0.259259259259\n  end\nend\n\n\nsubsection G - ParabolicModule\n  set tolerance             = 1e-16\n  set tolerance linfty norm = false\n\n  set multigrid velocity    = false\n  set multigrid energy      = false\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.30\n  set cfl max               = 0.30\n  set cfl recovery strategy = none\n  set time stepping scheme  = strang ssprk 33 cn\nend\n"
  },
  {
    "path": "tests/scalar_conservation/CMakeLists.txt",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2022 - 2024 by the ryujin authors\n##\n\nset(EQUATION scalar_conservation)\n\ninclude_directories(\n  ${CMAKE_BINARY_DIR}/source/\n  ${CMAKE_SOURCE_DIR}/source/${EQUATION}\n  ${CMAKE_SOURCE_DIR}/source/\n  )\n\nset(TEST_LIBRARIES obj_common obj_${EQUATION} obj_${EQUATION}_dependent)\nset(TEST_TARGET ryujin)\n\nif(TARGET obj_${EQUATION})\n  deal_ii_pickup_tests()\nendif()\n"
  },
  {
    "path": "tests/scalar_conservation/hyperbolic_system.cc",
    "content": "#include <hyperbolic_system.h>\n#include <simd.h>\n\n#include <iomanip>\n#include <iostream>\n\nusing namespace ryujin::ScalarConservation;\nusing namespace ryujin;\nusing namespace dealii;\n\n\ntemplate <int dim, typename Number>\nvoid test(const std::string &expression)\n{\n  std::cout << std::setprecision(10);\n  std::cout << std::scientific;\n\n  HyperbolicSystem hyperbolic_system;\n  const auto view = hyperbolic_system.view<dim, Number>();\n\n  {\n    std::stringstream parameters;\n    parameters << \"subsection HyperbolicSystem\\n\"\n               << \"set flux = \" << expression << \"\\n\"\n               << \"end\\n\"\n               << std::endl;\n    ParameterAcceptor::initialize(parameters);\n  }\n\n  using View = HyperbolicSystemView<dim, Number>;\n  using state_type = typename View::state_type;\n\n  state_type U{{1.4}};\n\n  std::cout << \"dim = \" << dim << std::endl;\n  std::cout << \"f(u)={\" + expression + \"}\" << std::endl;\n\n  const auto u = view.state(U);\n  std::cout << \"state = \" << u << std::endl;\n  std::cout << \"square_entropy = \" << view.square_entropy(u) << std::endl;\n  std::cout << \"square_entropy_derivative = \"\n            << view.square_entropy_derivative(u) << std::endl;\n  std::cout << \"flux = \" << view.flux_function(u) << std::endl;\n  std::cout << \"flux_gradient = \" << view.flux_gradient_function(u)\n            << std::endl;\n}\n\nint main()\n{\n  test<1, double>(\"burgers\");\n  dealii::ParameterAcceptor::clear();\n  test<1, float>(\"burgers\");\n  dealii::ParameterAcceptor::clear();\n  test<2, double>(\"burgers\");\n  dealii::ParameterAcceptor::clear();\n  test<2, float>(\"burgers\");\n  dealii::ParameterAcceptor::clear();\n\n  test<2, double>(\"kpp\");\n  dealii::ParameterAcceptor::clear();\n  test<2, float>({\"kpp\"});\n\n  return 0;\n}\n"
  },
  {
    "path": "tests/scalar_conservation/hyperbolic_system.threads=2.output",
    "content": "dim = 1\nf(u)={burgers}\nstate = 1.4000000000e+00\nsquare_entropy = 9.8000000000e-01\nsquare_entropy_derivative = 1.4000000000e+00\nflux = 9.8000000000e-01\nflux_gradient = 1.4000000000e+00\ndim = 1\nf(u)={burgers}\nstate = 1.3999999762e+00\nsquare_entropy = 9.7999995947e-01\nsquare_entropy_derivative = 1.3999999762e+00\nflux = 9.7999995947e-01\nflux_gradient = 1.3999999762e+00\ndim = 2\nf(u)={burgers}\nstate = 1.4000000000e+00\nsquare_entropy = 9.8000000000e-01\nsquare_entropy_derivative = 1.4000000000e+00\nflux = 9.8000000000e-01 9.8000000000e-01\nflux_gradient = 1.4000000000e+00 1.4000000000e+00\ndim = 2\nf(u)={burgers}\nstate = 1.3999999762e+00\nsquare_entropy = 9.7999995947e-01\nsquare_entropy_derivative = 1.3999999762e+00\nflux = 9.7999995947e-01 9.7999995947e-01\nflux_gradient = 1.3999999762e+00 1.3999999762e+00\ndim = 2\nf(u)={kpp}\nstate = 1.4000000000e+00\nsquare_entropy = 9.8000000000e-01\nsquare_entropy_derivative = 1.4000000000e+00\nflux = 9.8544972999e-01 1.6996714290e-01\nflux_gradient = 1.6996714290e-01 -9.8544972999e-01\ndim = 2\nf(u)={kpp}\nstate = 1.3999999762e+00\nsquare_entropy = 9.7999995947e-01\nsquare_entropy_derivative = 1.3999999762e+00\nflux = 9.8544973135e-01 1.6996715963e-01\nflux_gradient = 1.6996715963e-01 -9.8544973135e-01\n"
  },
  {
    "path": "tests/scalar_conservation/riemann_solver.cc",
    "content": "// force distinct symbols in test\n#define ScalarConservation ScalarConservationTest\n\n#include <hyperbolic_system.h>\n#include <multicomponent_vector.h>\n#define DEBUG_RIEMANN_SOLVER\n#include <riemann_solver.h>\n#include <riemann_solver.template.h>\n\n#include <iomanip>\n#include <iostream>\n\nusing namespace ryujin::ScalarConservation;\nusing namespace ryujin;\nusing namespace dealii;\n\n\ntemplate <int dim, typename Number>\nvoid test(const std::string &expression)\n{\n  std::cout << std::setprecision(10);\n  std::cout << std::scientific;\n\n  HyperbolicSystem hyperbolic_system;\n  typename RiemannSolver<dim, Number>::Parameters riemann_solver_parameters;\n\n  const auto view = hyperbolic_system.view<dim, Number>();\n\n  {\n    std::stringstream parameters;\n    parameters << \"subsection HyperbolicSystem\\n\"\n               << \"set flux = \" << expression << \"\\n\"\n               << \"end\\n\"\n               << \"subsection RiemannSolver\\n\"\n               << \"set use greedy wavespeed = true\\n\"\n               << \"set use averaged entropy = true\\n\"\n               << \"end\\n\"\n               << std::endl;\n    ParameterAcceptor::initialize(parameters);\n  }\n\n  using View = HyperbolicSystemView<dim, Number>;\n  using state_type = typename View::state_type;\n  using precomputed_type = typename View::precomputed_type;\n  static constexpr auto n_precomputed_values = View::n_precomputed_values;\n  using PrecomputedVector = typename View::PrecomputedVector;\n\n  PrecomputedVector dummy;\n\n  RiemannSolver<dim> riemann_solver(\n      hyperbolic_system, riemann_solver_parameters, dummy);\n\n  std::cout << \"\\n\\ndim = \" << dim << std::endl;\n  std::cout << \"f(u)={\" + expression + \"}\" << std::endl;\n\n  Number u_i{1.0};\n  const auto f_i = view.flux_function(u_i);\n  const auto df_i = view.flux_gradient_function(u_i);\n\n  Number u_j{2.0};\n  const auto f_j = view.flux_function(u_j);\n  const auto df_j = view.flux_gradient_function(u_j);\n\n  precomputed_type prec_i;\n  precomputed_type prec_j;\n\n  for (unsigned int k = 0; k < n_precomputed_values / 2; ++k) {\n    prec_i[k] = f_i[k];\n    prec_i[dim + k] = df_i[k];\n    prec_j[k] = f_j[k];\n    prec_j[dim + k] = df_j[k];\n  }\n\n  dealii::Tensor<1, dim, Number> n_ij;\n\n  if constexpr (dim == 1) {\n    n_ij[0] = 1.;\n    riemann_solver.compute(u_i, u_j, prec_i, prec_j, n_ij);\n\n    n_ij[0] = -1.;\n    riemann_solver.compute(u_i, u_j, prec_i, prec_j, n_ij);\n\n  } else if constexpr (dim == 2) {\n    n_ij[0] = 1.;\n    n_ij[1] = 0.;\n    riemann_solver.compute(u_i, u_j, prec_i, prec_j, n_ij);\n\n    n_ij[0] = 0.;\n    n_ij[1] = 1.;\n    riemann_solver.compute(u_i, u_j, prec_i, prec_j, n_ij);\n\n    n_ij[0] = 1.;\n    n_ij[1] = 1.;\n    n_ij /= n_ij.norm();\n    riemann_solver.compute(u_i, u_j, prec_i, prec_j, n_ij);\n  }\n}\n\nint main()\n{\n  test<1, double>(\"burgers\");\n  dealii::ParameterAcceptor::clear();\n  test<2, double>(\"burgers\");\n  dealii::ParameterAcceptor::clear();\n\n  test<2, double>(\"kpp\");\n\n  return 0;\n}\n"
  },
  {
    "path": "tests/scalar_conservation/riemann_solver.threads=2.output",
    "content": "\n\ndim = 1\nf(u)={burgers}\n\nu_i  = 1.0000000000e+00\nu_j  = 2.0000000000e+00\nf_i  = 5.0000000000e-01\nf_j  = 2.0000000000e+00\ndf_i = 1.0000000000e+00\ndf_j = 2.0000000000e+00\n   Roe average       = 1.5000000000e+00\n   interpolated      = 1.5000000000e+00\nk    = 1.5000000000e+00\nf_k  = 1.1250000000e+00\n   left  wavespeed   = 1.7499999996e+00\n   right wavespeed   = 1.2499999997e+00\n-> lambda_max        = 1.7499999996e+00\n\nu_i  = 1.0000000000e+00\nu_j  = 2.0000000000e+00\nf_i  = -5.0000000000e-01\nf_j  = -2.0000000000e+00\ndf_i = -1.0000000000e+00\ndf_j = -2.0000000000e+00\n   Roe average       = 1.5000000000e+00\n   interpolated      = 1.5000000000e+00\nk    = 1.5000000000e+00\nf_k  = -1.1250000000e+00\n   left  wavespeed   = 1.7499999996e+00\n   right wavespeed   = 1.2499999997e+00\n-> lambda_max        = 1.7499999996e+00\n\n\ndim = 2\nf(u)={burgers}\n\nu_i  = 1.0000000000e+00\nu_j  = 2.0000000000e+00\nf_i  = 5.0000000000e-01\nf_j  = 2.0000000000e+00\ndf_i = 1.0000000000e+00\ndf_j = 2.0000000000e+00\n   Roe average       = 1.5000000000e+00\n   interpolated      = 1.5000000000e+00\nk    = 1.5000000000e+00\nf_k  = 1.1250000000e+00\n   left  wavespeed   = 1.7499999996e+00\n   right wavespeed   = 1.2499999997e+00\n-> lambda_max        = 1.7499999996e+00\n\nu_i  = 1.0000000000e+00\nu_j  = 2.0000000000e+00\nf_i  = 5.0000000000e-01\nf_j  = 2.0000000000e+00\ndf_i = 1.0000000000e+00\ndf_j = 2.0000000000e+00\n   Roe average       = 1.5000000000e+00\n   interpolated      = 1.5000000000e+00\nk    = 1.5000000000e+00\nf_k  = 1.1250000000e+00\n   left  wavespeed   = 1.7499999996e+00\n   right wavespeed   = 1.2499999997e+00\n-> lambda_max        = 1.7499999996e+00\n\nu_i  = 1.0000000000e+00\nu_j  = 2.0000000000e+00\nf_i  = 7.0710678119e-01\nf_j  = 2.8284271247e+00\ndf_i = 1.4142135624e+00\ndf_j = 2.8284271247e+00\n   Roe average       = 2.1213203436e+00\n   interpolated      = 2.1213203436e+00\nk    = 1.5000000000e+00\nf_k  = 1.5909902577e+00\n   left  wavespeed   = 2.4748737337e+00\n   right wavespeed   = 1.7677669526e+00\n-> lambda_max        = 2.4748737337e+00\n\n\ndim = 2\nf(u)={kpp}\n\nu_i  = 1.0000000000e+00\nu_j  = 2.0000000000e+00\nf_i  = 8.4147098481e-01\nf_j  = 9.0929742683e-01\ndf_i = 5.4030230587e-01\ndf_j = -4.1614683655e-01\n   Roe average       = 6.7826442018e-02\n   interpolated      = 6.2077734660e-02\nk    = 1.5000000000e+00\nf_k  = 9.9749498660e-01\n   left  wavespeed   = 1.7639511952e-01\n   right wavespeed   = 3.1204800353e-01\n-> lambda_max        = 3.1204800353e-01\n\nu_i  = 1.0000000000e+00\nu_j  = 2.0000000000e+00\nf_i  = 5.4030230587e-01\nf_j  = -4.1614683655e-01\ndf_i = -8.4147098481e-01\ndf_j = -9.0929742683e-01\n   Roe average       = 9.5644914242e-01\n   interpolated      = 8.7538420582e-01\nk    = 1.5000000000e+00\nf_k  = 7.0737201668e-02\n   left  wavespeed   = 9.7376807623e-01\n   right wavespeed   = 9.3913020821e-01\n-> lambda_max        = 9.7376807623e-01\n\nu_i  = 1.0000000000e+00\nu_j  = 2.0000000000e+00\nf_i  = 9.7706126390e-01\nf_j  = 3.4871012653e-01\ndf_i = -2.1295841516e-01\ndf_j = -9.3723062672e-01\n   Roe average       = 6.2835113737e-01\n   interpolated      = 5.7509452094e-01\nk    = 1.5000000000e+00\nf_k  = 7.5535422421e-01\n   left  wavespeed   = 8.1328819519e-01\n   right wavespeed   = 4.4341407929e-01\n-> lambda_max        = 8.1328819519e-01\n"
  },
  {
    "path": "tests/scalar_conservation/verification-linear_transport-erk11.prm",
    "content": "subsection A - TimeLoop\n  set basename             = verification\n\n  set enable compute error = true\n  set error normalize      = true\n\n  set final time           = 2.00\n  set timer granularity    = 2.00\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = scalar conservation\n  set flux      = function\n  subsection function\n    set derivative approximation delta = 1e-10\n    set expression                     = u\n  end\nend\n\n\nsubsection C - Discretization\n  set finite element ansatz = cG Q1\n\n  set geometry            = rectangular domain\n\n  set mesh refinement     = 9\n\n  subsection rectangular domain\n    set boundary condition left   = periodic\n    set boundary condition right  = periodic\n    set position bottom left      = 0\n    set position top right        = 6.28318530718\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = function\n  set direction     = 1\n  set position      = 1\n\n  subsection function\n    set expression = sin(x-t)\n  end\nend\n\n\nsubsection F - HyperbolicModule\n  subsection indicator\n    set evc factor = 0\n  end\n  subsection limiter\n    set iterations        = 2\n    set relaxation factor = 1\n  end\n  subsection riemann solver\n    set use averaged entropy = false\n    set use greedy wavespeed = false\n    set random entropies = 0\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl max               = 0.05\n  set cfl min               = 0.05\n  set time stepping scheme  = erk 11\n  set cfl recovery strategy = none\nend\n"
  },
  {
    "path": "tests/scalar_conservation/verification-linear_transport-erk11.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »scalar conservation« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 513\nt     = 2.000003985764484\nLinf  = 0.0003068441603816815\nL1    = 0.0003068439412726019\nL2    = 0.0003068438024057874\n"
  },
  {
    "path": "tests/scalar_conservation/verification-linear_transport-erk22.prm",
    "content": "subsection A - TimeLoop\n  set basename             = verification\n\n  set enable compute error = true\n  set error normalize      = true\n\n  set final time           = 2.00\n  set timer granularity    = 2.00\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = scalar conservation\n  set flux      = function\n  subsection function\n    set derivative approximation delta = 1e-10\n    set expression                     = u\n  end\nend\n\n\nsubsection C - Discretization\n  set finite element ansatz = cG Q1\n\n  set geometry            = rectangular domain\n\n  set mesh refinement     = 9\n\n  subsection rectangular domain\n    set boundary condition left   = periodic\n    set boundary condition right  = periodic\n    set position bottom left      = 0\n    set position top right        = 6.28318530718\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = function\n  set direction     = 1\n  set position      = 1\n\n  subsection function\n    set expression = sin(x-t)\n  end\nend\n\n\nsubsection F - HyperbolicModule\n  subsection indicator\n    set evc factor = 0\n  end\n  subsection limiter\n    set iterations        = 2\n    set relaxation factor = 1\n  end\n  subsection riemann solver\n    set use averaged entropy = false\n    set use greedy wavespeed = false\n    set random entropies = 0\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl max               = 0.20\n  set cfl min               = 0.20\n  set time stepping scheme  = erk 22\n  set cfl recovery strategy = none\nend\n"
  },
  {
    "path": "tests/scalar_conservation/verification-linear_transport-erk22.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »scalar conservation« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 513\nt     = 2.000310781896445\nLinf  = 2.00679417503173e-06\nL1    = 2.006781459505022e-06\nL2    = 2.006775372663651e-06\n"
  },
  {
    "path": "tests/scalar_conservation/verification-linear_transport-erk33.prm",
    "content": "subsection A - TimeLoop\n  set basename             = verification\n\n  set enable compute error = true\n  set error normalize      = true\n\n  set final time           = 2.00\n  set timer granularity    = 2.00\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = scalar conservation\n  set flux      = function\n  subsection function\n    set derivative approximation delta = 1e-10\n    set expression                     = u\n  end\nend\n\n\nsubsection C - Discretization\n  set finite element ansatz = cG Q1\n\n  set geometry            = rectangular domain\n\n  set mesh refinement     = 9\n\n  subsection rectangular domain\n    set boundary condition left   = periodic\n    set boundary condition right  = periodic\n    set position bottom left      = 0\n    set position top right        = 6.28318530718\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = function\n  set direction     = 1\n  set position      = 1\n\n  subsection function\n    set expression = sin(x-t)\n  end\nend\n\n\nsubsection F - HyperbolicModule\n  subsection indicator\n    set evc factor = 0\n  end\n  subsection limiter\n    set iterations        = 2\n    set relaxation factor = 1\n  end\n  subsection riemann solver\n    set use averaged entropy = false\n    set use greedy wavespeed = false\n    set random entropies = 0\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl max               = 0.80\n  set cfl min               = 0.80\n  set time stepping scheme  = erk 33\n  set cfl recovery strategy = none\nend\n"
  },
  {
    "path": "tests/scalar_conservation/verification-linear_transport-erk33.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »scalar conservation« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 513\nt     = 2.00276515095403\nLinf  = 2.66483777805169e-07\nL1    = 2.664823400327689e-07\nL2    = 2.664824960792129e-07\n"
  },
  {
    "path": "tests/scalar_conservation/verification-linear_transport-erk43.prm",
    "content": "subsection A - TimeLoop\n  set basename             = verification\n\n  set enable compute error = true\n  set error normalize      = true\n\n  set final time           = 2.00\n  set timer granularity    = 2.00\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = scalar conservation\n  set flux      = function\n  subsection function\n    set derivative approximation delta = 1e-10\n    set expression                     = u\n  end\nend\n\n\nsubsection C - Discretization\n  set finite element ansatz = cG Q1\n\n  set geometry            = rectangular domain\n\n  set mesh refinement     = 9\n\n  subsection rectangular domain\n    set boundary condition left   = periodic\n    set boundary condition right  = periodic\n    set position bottom left      = 0\n    set position top right        = 6.28318530718\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = function\n  set direction     = 1\n  set position      = 1\n\n  subsection function\n    set expression = sin(x-t)\n  end\nend\n\n\nsubsection F - HyperbolicModule\n  subsection indicator\n    set evc factor = 0\n  end\n  subsection limiter\n    set iterations        = 2\n    set relaxation factor = 1\n  end\n  subsection riemann solver\n    set use averaged entropy = false\n    set use greedy wavespeed = false\n    set random entropies = 0\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl max               = 0.80\n  set cfl min               = 0.80\n  set time stepping scheme  = erk 43\n  set cfl recovery strategy = none\nend\n"
  },
  {
    "path": "tests/scalar_conservation/verification-linear_transport-erk43.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »scalar conservation« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 513\nt     = 2.00276515095403\nLinf  = 3.994607054456684e-09\nL1    = 3.994684035874167e-09\nL2    = 3.994676197321224e-09\n"
  },
  {
    "path": "tests/scalar_conservation/verification-linear_transport-erk54.prm",
    "content": "subsection A - TimeLoop\n  set basename             = verification\n\n  set enable compute error = true\n  set error normalize      = true\n\n  set final time           = 2.00\n  set timer granularity    = 2.00\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = scalar conservation\n  set flux      = function\n  subsection function\n    set derivative approximation delta = 1e-10\n    set expression                     = u\n  end\nend\n\n\nsubsection C - Discretization\n  set finite element ansatz = cG Q1\n\n  set geometry            = rectangular domain\n\n  set mesh refinement     = 9\n\n  subsection rectangular domain\n    set boundary condition left   = periodic\n    set boundary condition right  = periodic\n    set position bottom left      = 0\n    set position top right        = 6.28318530718\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = function\n  set direction     = 1\n  set position      = 1\n\n  subsection function\n    set expression = sin(x-t)\n  end\nend\n\n\nsubsection F - HyperbolicModule\n  subsection indicator\n    set evc factor = 0\n  end\n  subsection limiter\n    set iterations        = 2\n    set relaxation factor = 1\n  end\n  subsection riemann solver\n    set use averaged entropy = false\n    set use greedy wavespeed = false\n    set random entropies = 0\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl max               = 0.80\n  set cfl min               = 0.80\n  set time stepping scheme  = erk 54\n  set cfl recovery strategy = none\nend\n"
  },
  {
    "path": "tests/scalar_conservation/verification-linear_transport-erk54.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »scalar conservation« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 513\nt     = 2.012582627184197\nLinf  = 7.943007435700865e-09\nL1    = 7.942987312724608e-09\nL2    = 7.942961632744394e-09\n"
  },
  {
    "path": "tests/scalar_conservation/verification-linear_transport-ssprk22.prm",
    "content": "subsection A - TimeLoop\n  set basename             = verification\n\n  set enable compute error = true\n  set error normalize      = true\n\n  set final time           = 2.00\n  set timer granularity    = 2.00\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = scalar conservation\n  set flux      = function\n  subsection function\n    set derivative approximation delta = 1e-10\n    set expression                     = u\n  end\nend\n\n\nsubsection C - Discretization\n  set finite element ansatz = cG Q1\n\n  set geometry            = rectangular domain\n\n  set mesh refinement     = 9\n\n  subsection rectangular domain\n    set boundary condition left   = periodic\n    set boundary condition right  = periodic\n    set position bottom left      = 0\n    set position top right        = 6.28318530718\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = function\n  set direction     = 1\n  set position      = 1\n\n  subsection function\n    set expression = sin(x-t)\n  end\nend\n\n\nsubsection F - HyperbolicModule\n  subsection indicator\n    set evc factor = 0\n  end\n  subsection limiter\n    set iterations        = 2\n    set relaxation factor = 1\n  end\n  subsection riemann solver\n    set use averaged entropy = false\n    set use greedy wavespeed = false\n    set random entropies = 0\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl max               = 0.80\n  set cfl min               = 0.80\n  set time stepping scheme  = ssprk 22\n  set cfl recovery strategy = none\nend\n"
  },
  {
    "path": "tests/scalar_conservation/verification-linear_transport-ssprk22.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »scalar conservation« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 513\nt     = 2.002765150954031\nLinf  = 8.041458851773748e-06\nL1    = 8.041483759870051e-06\nL2    = 8.041490336054372e-06\n"
  },
  {
    "path": "tests/scalar_conservation/verification-linear_transport-ssprk33.prm",
    "content": "subsection A - TimeLoop\n  set basename             = verification\n\n  set enable compute error = true\n  set error normalize      = true\n\n  set final time           = 2.00\n  set timer granularity    = 2.00\n\n  set terminal update interval  = 0\nend\n\n\nsubsection B - Equation\n  set dimension = 1\n  set equation  = scalar conservation\n  set flux      = function\n  subsection function\n    set derivative approximation delta = 1e-10\n    set expression                     = u\n  end\nend\n\n\nsubsection C - Discretization\n  set finite element ansatz = cG Q1\n\n  set geometry            = rectangular domain\n\n  set mesh refinement     = 9\n\n  subsection rectangular domain\n    set boundary condition left   = periodic\n    set boundary condition right  = periodic\n    set position bottom left      = 0\n    set position top right        = 6.28318530718\n  end\nend\n\n\nsubsection E - InitialValues\n  set configuration = function\n  set direction     = 1\n  set position      = 1\n\n  subsection function\n    set expression = sin(x-t)\n  end\nend\n\n\nsubsection F - HyperbolicModule\n  subsection indicator\n    set evc factor = 0\n  end\n  subsection limiter\n    set iterations        = 2\n    set relaxation factor = 1\n  end\n  subsection riemann solver\n    set use averaged entropy = false\n    set use greedy wavespeed = false\n    set random entropies = 0\n  end\nend\n\n\nsubsection H - TimeIntegrator\n  set cfl max               = 0.80\n  set cfl min               = 0.80\n  set time stepping scheme  = ssprk 33\n  set cfl recovery strategy = none\nend\n"
  },
  {
    "path": "tests/scalar_conservation/verification-linear_transport-ssprk33.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »scalar conservation« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 513\nt     = 2.002765150954031\nLinf  = 9.979936186466348e-09\nL1    = 9.979772392032799e-09\nL2    = 9.979813202693059e-09\n"
  },
  {
    "path": "tests/scripts/print_mass",
    "content": "#!/bin/bash\n\n#\n# Workaround: Only print the last 5 entries of the second column of the\n# quantities output file.\n#\n\nawk '{print $2}' \"${@}\" | tail -5\n"
  },
  {
    "path": "tests/shallow_water/CMakeLists.txt",
    "content": "##\n## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n## Copyright (C) 2022 - 2024 by the ryujin authors\n##\n\nset(EQUATION shallow_water)\n\ninclude_directories(\n  ${CMAKE_BINARY_DIR}/source/\n  ${CMAKE_SOURCE_DIR}/source/${EQUATION}\n  ${CMAKE_SOURCE_DIR}/source/\n  )\n\nset(TEST_LIBRARIES obj_common obj_${EQUATION} obj_${EQUATION}_dependent)\nset(TEST_TARGET ryujin)\n\nif(TARGET obj_${EQUATION})\n  deal_ii_pickup_tests()\nendif()\n"
  },
  {
    "path": "tests/shallow_water/riemann_solver.cc",
    "content": "// force distinct symbols in test\n#define ShallowWater ShallowWaterTest\n\n#include <hyperbolic_system.h>\n#include <multicomponent_vector.h>\n#define DEBUG_RIEMANN_SOLVER\n#include <riemann_solver.h>\n#include <riemann_solver.template.h>\n#include <simd.h>\n\nusing namespace ryujin::ShallowWater;\nusing namespace ryujin;\nusing namespace dealii;\n\nint main()\n{\n  constexpr int dim = 1;\n\n  HyperbolicSystem hyperbolic_system;\n  const double gravity = hyperbolic_system.view<dim, double>().gravity();\n\n  RiemannSolver<dim, double>::Parameters riemann_solver_parameters;\n\n  static constexpr unsigned int n_precomputed_values =\n      HyperbolicSystemView<dim, double>::n_precomputed_values;\n  using precomputed_type =\n      Vectors::MultiComponentVector<double, n_precomputed_values>;\n  precomputed_type dummy;\n\n  RiemannSolver<dim> riemann_solver(\n      hyperbolic_system, riemann_solver_parameters, dummy);\n\n  const auto riemann_data = [&](const auto &state) {\n    const auto h = hyperbolic_system.view<dim, double>().water_depth_sharp(\n        dealii::Tensor<1, 2, double>{{state[0], state[1]}});\n    const double u = state[1] / h;\n\n    std::array<double, 3> result;\n    result[0] = h;\n    result[1] = u;\n    result[2] = std::sqrt(gravity * h);\n    return result;\n  };\n\n  const auto test = [&](const std::array<double, 2> &U_i,\n                        const std::array<double, 2> &U_j) {\n    std::cout << U_i[0] << \" \" << U_i[1] << std::endl;\n    std::cout << U_j[0] << \" \" << U_j[1] << std::endl;\n    const auto rd_i = riemann_data(U_i);\n    const auto rd_j = riemann_data(U_j);\n    std::cout << \"relaxation: \" << rd_i[0] << \" \" << rd_i[1] << std::endl;\n    std::cout << \"relaxation: \" << rd_j[0] << \" \" << rd_j[1] << std::endl;\n    const auto h_star = riemann_solver.compute_h_star(rd_i, rd_j);\n    const auto lambda_max = riemann_solver.compute(rd_i, rd_j);\n    std::cout << \"lambda_max: \" << lambda_max << std::endl;\n    std::cout << \"h_star: \" << h_star << std::endl;\n    std::cout << std::endl;\n  };\n\n  std::cout << std::setprecision(16);\n  std::cout << std::scientific;\n\n  const auto view = hyperbolic_system.view<dim, double>();\n\n  std::cout << \"gravity:                      \" << gravity << std::endl;\n  std::cout << \"reference_water_depth:        \" << view.reference_water_depth()\n            << std::endl;\n  std::cout << \"dry_state_relaxation (small): \"\n            << view.dry_state_relaxation_small() << std::endl;\n  std::cout << \"dry_state_relaxation (large): \"\n            << view.dry_state_relaxation_large() << std::endl;\n  std::cout << std::endl;\n\n  // 10/04/2022 verified against Mathematica computation (Eric + Matthias)\n  test({0.0, 0.0}, {0.0, 0.0});\n  test({1.0, 1.0}, {0.0, 0.0});\n  test({1.8, 0.0}, {1.0, 0.0});\n\n  return 0;\n}\n"
  },
  {
    "path": "tests/shallow_water/riemann_solver.threads=2.output",
    "content": "gravity:                      9.8100000000000005e+00\nreference_water_depth:        1.0000000000000000e+00\ndry_state_relaxation (small): 1.0000000000000000e+02\ndry_state_relaxation (large): 1.0000000000000000e+04\n\n0.0000000000000000e+00 0.0000000000000000e+00\n0.0000000000000000e+00 0.0000000000000000e+00\nrelaxation: 2.2204460492503131e-14 0.0000000000000000e+00\nrelaxation: 2.2204460492503131e-14 0.0000000000000000e+00\n2.2204460492503131e-14  <- h_min/max ->  2.2204460492503131e-14\n4.6671807060735897e-07  <- a_min/max ->  4.6671807060735897e-07\nf_L --> 8.8138056330834387e-07\nf_R --> 8.8138056330834387e-07\nphi_value_min ->1.7627611266166877e-06\nf_L --> 8.8138056330834387e-07\nf_R --> 8.8138056330834387e-07\nphi_value_max ->1.7627611266166877e-06\nleft: 2.2204460492503128e-14\nmiddle: 2.2204460492503131e-14\nright: 2.2204460492503131e-14\n2.2204460492503131e-14  <- h_min/max ->  2.2204460492503131e-14\n4.6671807060735897e-07  <- a_min/max ->  4.6671807060735897e-07\nf_L --> 8.8138056330834387e-07\nf_R --> 8.8138056330834387e-07\nphi_value_min ->1.7627611266166877e-06\nf_L --> 8.8138056330834387e-07\nf_R --> 8.8138056330834387e-07\nphi_value_max ->1.7627611266166877e-06\nleft: 2.2204460492503128e-14\nmiddle: 2.2204460492503131e-14\nright: 2.2204460492503131e-14\nlambda_max: 4.6671807060735897e-07\nh_star: 2.2204460492503131e-14\n\n1.0000000000000000e+00 1.0000000000000000e+00\n0.0000000000000000e+00 0.0000000000000000e+00\nrelaxation: 1.0000000000000000e+00 1.0000000000000000e+00\nrelaxation: 2.2204460492503131e-14 0.0000000000000000e+00\n2.2204460492503131e-14  <- h_min/max ->  1.0000000000000000e+00\n4.6671807060735897e-07  <- a_min/max ->  3.1320919526731652e+00\nf_L --> -6.2641821986263704e+00\nf_R --> 8.8138056330834387e-07\nphi_value_min ->-7.2641813172458072e+00\nf_L --> 5.9148448355303032e+00\nf_R --> 4.9688364714015745e+07\nphi_value_max ->4.9688369628860578e+07\nleft: 3.3618999345054223e-01\nmiddle: 2.1629386748425296e-07\nright: 4.8845621006836883e-07\n2.2204460492503131e-14  <- h_min/max ->  1.0000000000000000e+00\n4.6671807060735897e-07  <- a_min/max ->  3.1320919526731652e+00\nf_L --> -6.2641821986263704e+00\nf_R --> 8.8138056330834387e-07\nphi_value_min ->-7.2641813172458072e+00\nf_L --> 5.9148448355303032e+00\nf_R --> 4.9688364714015745e+07\nphi_value_max ->4.9688369628860578e+07\nleft: 3.3618999345054223e-01\nmiddle: 2.1629386748425296e-07\nright: 4.8845621006836883e-07\nlambda_max: 7.2598063846511982e+00\nh_star: 4.8845621006836883e-07\n\n1.8000000000000000e+00 0.0000000000000000e+00\n1.0000000000000000e+00 0.0000000000000000e+00\nrelaxation: 1.8000000000000000e+00 0.0000000000000000e+00\nrelaxation: 1.0000000000000000e+00 0.0000000000000000e+00\n1.0000000000000000e+00  <- h_min/max ->  1.8000000000000000e+00\n3.1320919526731652e+00  <- a_min/max ->  4.2021423107743505e+00\nf_L --> 3.1595625754969578e+00\nf_R --> 5.9148448355303032e+00\nphi_value_min ->9.0744074110272610e+00\nf_L --> 7.9355970771655944e+00\nf_R --> 1.2000608437434918e+01\nphi_value_max ->1.9936205514600513e+01\nleft: 1.3708203932499368e+00\nmiddle: 1.3416407864998738e+00\nright: 1.4219545072274116e+00\n1.0000000000000000e+00  <- h_min/max ->  1.8000000000000000e+00\n3.1320919526731652e+00  <- a_min/max ->  4.2021423107743505e+00\nf_L --> 3.1595625754969578e+00\nf_R --> 5.9148448355303032e+00\nphi_value_min ->9.0744074110272610e+00\nf_L --> 7.9355970771655944e+00\nf_R --> 1.2000608437434918e+01\nphi_value_max ->1.9936205514600513e+01\nleft: 1.3708203932499368e+00\nmiddle: 1.3416407864998738e+00\nright: 1.4219545072274116e+00\nlambda_max: 4.2021423107743505e+00\nh_star: 1.4219545072274116e+00\n\n"
  },
  {
    "path": "tests/shallow_water/verification-paraboloid_1d-erk33-l7.prm",
    "content": "##\n#\n# Shallow water benchmark:\n#\n# A 1D benchmark configuration consisting of a planar surface flow in a\n# radially-symmetric paraboloid basin without friction. See Section 4.2.2\n# of [1] for details.\n#\n# This configuration uses an explicit ERK(3, 3, 1) timestepping. Expected\n# results are reported in shallow_water-paraboloid_1d-erk33.baseline\n#\n# [1] SWASHES: a compilation of shallow water analytic solutions for\n#     hydraulic and environmental studies, Olivier Delestre, et al.,\n#     International Journal for Numerical Methods in Fluids, Vol. 72(3), 2013\n#\n##\n\nsubsection A - TimeLoop\n  set basename             = paraboloid_1d-erk33\n\n  set enable compute error = true\n  set error normalize      = true\n  set error quantities     = h\n\n  set final time           = 1345.71\n  set timer granularity    = 1345.71\n\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension                    = 1\n  set equation                     = shallow water\n\n  set gravity                      = 9.81\n  set manning friction coefficient = 0\n\n  set reference water depth        = 10\n  set dry state relaxation small   = 1e2\n  set dry state relaxation large   = 1e4\nend\n\nsubsection C - Discretization\n  set geometry            = rectangular domain\n  set mesh refinement     = 7\n\n  subsection rectangular domain\n    set boundary condition left   = do nothing\n    set boundary condition right  = do nothing\n\n    set position bottom left      = 0\n    set position top right        = 10000\n    set subdivisions x            = 25\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = paraboloid\n  set direction     = 1\n  set position      = 0\n\n  subsection paraboloid\n    set free surface radius = 3000\n    set paraboloid length   = 10000\n    set speed               = 2\n    set water height        = 10\n  end\nend\n\nsubsection F - HyperbolicModule\n  subsection limiter\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.50\n  set cfl max               = 0.50\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/shallow_water/verification-paraboloid_1d-erk33-l7.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »shallow water« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 3201\nt     = 1345.892705834121\nLinf  = 0.0007376620042968423\nL1    = 0.0001350884991166228\nL2    = 0.0001623182815293719\n"
  },
  {
    "path": "tests/shallow_water/verification-paraboloid_1d-erk33-l7.threads=2.output.gcc-13.3-avx2",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »shallow water« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 3201\nt     = 1345.843239473931\nLinf  = 0.0001134637418615348\nL1    = 1.51703165081147e-05\nL2    = 1.852484788707163e-05\n"
  },
  {
    "path": "tests/shallow_water/verification-ritter_dam_break-erk33-l7.prm",
    "content": "##\n#\n# Shallow water benchmark:\n#\n# Ritter's 1D dam break solution without friction is a one-dimensional\n# analytical solution. See Section 7.3 in [1]\n#\n# This configuration uses an explicit ERK(3, 3, 1) timestepping. Expected\n# results are reported in shallow_water_ritter_dam_break-erk33.baseline\n#\n# [1] Well-Balanced Second-Order Finite Element Approximation of the\n#     Shallow Water Equations with Friction, Jean-Luc Guermond et al., Vol.\n#     40(6), 2018\n#\n##\n\nsubsection A - TimeLoop\n  set basename             = ritter_dam_break-erk33\n\n  set enable compute error = true\n  set error normalize      = true\n  set error quantities     = h\n\n  set final time           = 6.0\n  set timer granularity    = 6.0\n\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension                    = 1\n  set equation                     = shallow water\n\n  set gravity                      = 9.81\n  set manning friction coefficient = 0\n\n  set reference water depth        = 0.005\n  set dry state relaxation small   = 1e2\n  set dry state relaxation large   = 1e4\nend\n\nsubsection C - Discretization\n  set geometry            = rectangular domain\n  set mesh refinement     = 7\n\n  subsection rectangular domain\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n\n    set position bottom left      = 0\n    set position top right        = 10\n    set subdivisions x            = 25\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = ritter dam break\n  set direction     = 1\n  set position      = 5\n\n  subsection ritter dam break\n    set time initial = 1\n    set left water depth = 0.005\n  end\nend\n\nsubsection F - HyperbolicModule\n  subsection limiter\n    set relaxation factor = 0.0\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.5\n  set cfl max               = 0.5\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/shallow_water/verification-ritter_dam_break-erk33-l7.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »shallow water« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 3201\nt     = 6.000850933111626\nLinf  = 0.001119328882607552\nL1    = 2.555359607626231e-05\nL2    = 7.256012940299663e-05\n"
  },
  {
    "path": "tests/shallow_water/verification-smooth_vortex-erk33-l6.prm",
    "content": "##\n#\n# Shallow water benchmark:\n#\n# The smooth vortex is an analytic solution of the shallow water equations.\n# We compute the vortex on the square [-5, 5]^2 initially centered at\n# (-1,-1) with a strength of beta=5, and moving in diagonal (1,1) direction\n# with mach number 1. At final time t=2 the vortex is located at (1, 1). We\n# report the final, normalized L1, L2, L\\infty error norms summed up over\n# all components.\n#\n# This configuration uses an explicit ERK(3, 3, 1) timestepping.\n# Expected results are reported in euler-isentropic_vortex-erk33.baseline\n#\n##\n\nsubsection A - TimeLoop\n  set basename             = smooth_vortex-erk33\n\n  set enable compute error = true\n  set error normalize      = true\n  set error quantities     = h, m_1, m_2\n\n  set final time           = 2.0\n  set timer granularity    = 2.0\n\n  set terminal update interval  = 0\nend\n\nsubsection B - Equation\n  set dimension                    = 2\n  set equation                     = shallow water\n\n  set gravity                      = 9.81\n  set manning friction coefficient = 0\n\n  set reference water depth        = 2\n  set dry state relaxation small   = 1e2\n  set dry state relaxation large   = 1e4\nend\n\nsubsection C - Discretization\n  set geometry            = rectangular domain\n  set mesh refinement     = 6\n\n  subsection rectangular domain\n    set boundary condition bottom = dirichlet\n    set boundary condition left   = dirichlet\n    set boundary condition right  = dirichlet\n    set boundary condition top    = dirichlet\n\n    set position bottom left      = -6, -6\n    set position top right        =  6,  6\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = smooth vortex\n  set direction     =  1,  1\n  set position      = -1, -1\n\n  subsection smooth vortex\n    set beta            = 2\n    set mach number     = 1\n    set reference depth = 2\n  end\nend\n\nsubsection F - HyperbolicModule\n  subsection limiter\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl min               = 0.25\n  set cfl max               = 0.25\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n"
  },
  {
    "path": "tests/shallow_water/verification-smooth_vortex-erk33-l6.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »shallow water« with dim=2\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time \n#dofs = 4225\nt     = 2.001005728507654\nLinf  = 0.03571394823661699\nL1    = 0.0006325612013505061\nL2    = 0.003420776846038435\n"
  },
  {
    "path": "tests/shallow_water/verification-steady_incline-erk33-l9.prm",
    "content": "##\n#\n# Shallow water benchmark\n#\n# A 1D benchmark configuration consisting of a steady flow over an inclined \n# plane with Manning's friction. See section 4.1 of [1] for details.\n#\n# This configuration uses an explicit ERK(3, 3, 1) timestepping. Expected\n# results are reported in shallow_water-steady_incline_1d-erk33-l9.baseline\n#\n# [1] Chertock, Alina, et al. \"Well‐balanced positivity preserving \n#     central‐upwind scheme for the shallow water system with friction terms.\" \n#     International Journal for numerical methods in fluids \n#     78.6 (2015): 355-383.\n##\n\nsubsection A - TimeLoop\n  set basename                      = steady_incline_1d-erk33-l9\n\n  set enable compute error          = true\n  set error quantities              = h, m\n  set error normalize               = true\n\n  set final time                    = 1.0\n  set timer granularity             = 1.0\n\n  set terminal update interval      = 0\n\nend\n\nsubsection B - Equation\n  set dimension                    = 1\n  set equation                     = shallow water\n\n  set gravity                      = 9.81\n  set manning friction coefficient = 1.e-2\n\n  set reference water depth        = 1\n  set dry state relaxation large   = 1e4\n  set dry state relaxation small   = 1e4\nend\n\nsubsection C - Discretization\n  set geometry            = rectangular domain\n  set mesh refinement     = 9\n\n  subsection rectangular domain\n    set boundary condition left   = dynamic\n    set boundary condition right  = dynamic\n    set position bottom left      = 0\n    set position top right        = 20\n  end\nend\n\nsubsection E - InitialValues\n  set configuration = sloping friction\n  set position      = 0\n\n  subsection sloping friction\n   set ramp slope        = 1.e-2\n   set initial discharge = 1.e-1\n  end\nend\n\nsubsection F - HyperbolicModule\n  subsection limiter\n  end\nend\n\nsubsection H - TimeIntegrator\n  set cfl max               = 0.50\n  set cfl min               = 0.50\n  set cfl recovery strategy = none\n  set time stepping scheme  = erk 33\nend\n\nsubsection J - VTUOutput\n  set manifolds                  = \n  set schlieren beta             = 10\n  set schlieren quantities       = h\n  set schlieren recompute bounds = true\n  set use mpi io                 = true\n  set vtu output quantities      = h, m, bathymetry, alpha, eta_m\nend\n"
  },
  {
    "path": "tests/shallow_water/verification-steady_incline-erk33-l9.threads=2.output",
    "content": "[INFO] initiating flux capacitor\n[INFO] dispatching to driver »shallow water« with dim=1\n[INFO] initializing data structures\n[INFO] creating mesh and interpolating initial values\n[INFO] preparing compute kernels\n[INFO] entering main loop\nNormalized consolidated Linf, L1, and L2 errors at final time\n#dofs = 513\nt     = 1.000593578808362\nLinf  = 2.388278346583212e-14\nL1    = 4.287451614926996e-15\nL2    = 5.452329602107318e-15\n"
  }
]